diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index b38af61df6..27d967637d 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -1,77 +1,77 @@ set(TOOL_CPPS "") # temporary suppress warnings in the following files until image accessors are fully integrated. set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) #if(MSVC) # set(optional_private_package_depends psapi) #endif() mitk_create_module( INCLUDE_DIRS PUBLIC ${MITK_BINARY_DIR} PRIVATE src/Algorithms src/Controllers src/DataManagement src/Interactions src/IO src/Rendering DEPENDS PUBLIC mbilog CppMicroServices PACKAGE_DEPENDS PUBLIC Boost ITK|IOImageBase+SpatialObjects+Statistics #ITK|Statistics+Transform VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOXML PRIVATE - ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML + ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML tinyxml2 ${optional_private_package_depends} # Do not automatically create CppMicroServices initialization code. # Because the VTK "auto-init" functionality injects file-local static # initialization code in every cpp file which includes a VTK header, # static initialization order becomes an issue again. For the Mitk # core library, we need to ensure that the VTK static initialization stuff # happens before the CppMicroServices initialization, since the latter # might already use VTK code which needs to access VTK object factories. # Hence, CppMicroServices initialization code is placed manually within # the mitkCoreActivator.cpp file. NO_INIT ) if(NOT TARGET ${MODULE_TARGET}) message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist") endif() function(_itk_create_factory_register_manager) # In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which # prevents multiple registrations/unregistrations of ITK IO factories # during library loading/unloading (of MITK libraries). However, we need # "one" place where the IO factories are registered at # least once. This could be the application executable, but every executable would # need to take care of that itself. Instead, we allow the auto registration in the # Mitk Core library. set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1) find_package(ITK) include(${ITK_USE_FILE}) if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER) # We manually add the define which will be of target scope. MITK # patches ITK_USE_FILE to remove the directory scoped compile # definition since it would be propagated to other targets in the # same directory scope but these targets might want to *not* # use the ITK factory manager stuff. target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER) endif() endfunction() _itk_create_factory_register_manager() if(BUILD_TESTING) add_subdirectory(TestingHelper) add_subdirectory(test) endif() diff --git a/Modules/Core/include/itkImportMitkImageContainer.h b/Modules/Core/include/itkImportMitkImageContainer.h index 59f79e4ab4..1343566f22 100644 --- a/Modules/Core/include/itkImportMitkImageContainer.h +++ b/Modules/Core/include/itkImportMitkImageContainer.h @@ -1,102 +1,98 @@ /*============================================================================ 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 __itkImportMitkImageContainer_h #define __itkImportMitkImageContainer_h #include #include #include namespace itk { /** \class ImportMitkImageContainer * Defines an itk::Image front-end to an mitk::Image. This container * conforms to the ImageContainerInterface. This is a full-fleged Object, * so there is modification time, debug, and reference count information. * * Template parameters for ImportMitkImageContainer: * * TElementIdentifier = * An INTEGRAL type for use in indexing the imported buffer. * * TElement = * The element type stored in the container. */ template class ImportMitkImageContainer : public ImportImageContainer { public: /** Standard class typedefs. */ typedef ImportMitkImageContainer Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Save the template parameters. */ typedef TElementIdentifier ElementIdentifier; typedef TElement Element; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Standard part of every itk Object. */ itkTypeMacro(ImportMitkImageContainer, ImportImageContainer); ///** Get the pointer from which the image data is imported. */ // TElement *GetImportPointer() {return m_ImportPointer;}; /** \brief Set the mitk::ImageDataItem to be imported */ // void SetImageDataItem(mitk::ImageDataItem* imageDataItem); void SetImageAccessor(mitk::ImageAccessorBase *imageAccess, size_t noBytes); protected: ImportMitkImageContainer(); ~ImportMitkImageContainer() override; /** PrintSelf routine. Normally this is a protected internal method. It is * made public here so that Image can call this method. Users should not * call this method but should call Print() instead. */ void PrintSelf(std::ostream &os, Indent indent) const override; private: ImportMitkImageContainer(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented // mitk::ImageDataItem::Pointer m_ImageDataItem; mitk::ImageAccessorBase *m_imageAccess; }; } // end namespace itk // Define instantiation macro for this template. #define ITK_TEMPLATE_ImportMitkImageContainer(_, EXPORT, x, y) \ namespace itk \ { \ _(2(class EXPORT ImportMitkImageContainer)) \ namespace Templates \ { \ typedef ImportMitkImageContainer ImportMitkImageContainer##y; \ } \ } -//#if ITK_TEMPLATE_EXPLICIT -//# include "Templates/itkImportMitkImageContainer+-.h" -//#endif - -#if ITK_TEMPLATE_TXX +#ifndef ITK_MANUAL_INSTANTIATION #include "itkImportMitkImageContainer.txx" #endif #endif diff --git a/Modules/Core/include/itkVtkAbstractTransform.h b/Modules/Core/include/itkVtkAbstractTransform.h index 54ff4715b7..e4fc4c5917 100644 --- a/Modules/Core/include/itkVtkAbstractTransform.h +++ b/Modules/Core/include/itkVtkAbstractTransform.h @@ -1,101 +1,101 @@ /*============================================================================ 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 MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C #define MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C #include "itkTransform.h" #include class vtkAbstractTransform; namespace itk { //##Documentation //## @brief Adapter from vtkAbstractTransform to itk::Transform //## @ingroup Geometry template class VtkAbstractTransform : public itk::Transform { public: typedef VtkAbstractTransform Self; typedef Transform Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef typename Superclass::OutputPointType OutputPointType; typedef typename Superclass::OutputVectorType OutputVectorType; typedef typename Superclass::OutputVnlVectorType OutputVnlVectorType; typedef typename Superclass::OutputCovariantVectorType OutputCovariantVectorType; typedef typename Superclass::InputPointType InputPointType; typedef typename Superclass::InputVectorType InputVectorType; typedef typename Superclass::InputVnlVectorType InputVnlVectorType; typedef typename Superclass::InputCovariantVectorType InputCovariantVectorType; typedef typename Superclass::ParametersType ParametersType; typedef typename Superclass::JacobianType JacobianType; itkFactorylessNewMacro(Self); itkCloneMacro(Self); //##Documentation //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual vtkAbstractTransform *GetVtkAbstractTransform() const; //##Documentation //## @brief Get the inverse vtkAbstractTransform (stored in m_InverseVtkAbstractTransform) virtual vtkAbstractTransform *GetInverseVtkAbstractTransform() const; //##Documentation //## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual void SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform); using Superclass::TransformVector; using Superclass::TransformCovariantVector; OutputPointType TransformPoint(const InputPointType &) const override; OutputVectorType TransformVector(const InputVectorType &) const override; OutputVnlVectorType TransformVector(const InputVnlVectorType &) const override; OutputCovariantVectorType TransformCovariantVector(const InputCovariantVectorType &) const override; virtual InputPointType BackTransform(const OutputPointType &point) const; virtual InputVectorType BackTransform(const OutputVectorType &vector) const; virtual InputVnlVectorType BackTransform(const OutputVnlVectorType &vector) const; virtual InputCovariantVectorType BackTransform(const OutputCovariantVectorType &vector) const; void SetParameters(const ParametersType &) override; void SetFixedParameters(const ParametersType &) override; void ComputeJacobianWithRespectToParameters(const InputPointType &, JacobianType &) const override; - void ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianType &) const override; + void ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianPositionType &) const override; - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; protected: VtkAbstractTransform(); ~VtkAbstractTransform() override; //##Documentation //## @brief Instance of the vtkAbstractTransform vtkAbstractTransform *m_VtkAbstractTransform; //##Documentation //## @brief Instance of the vtkAbstractTransform vtkAbstractTransform *m_InverseVtkAbstractTransform; mutable unsigned long m_LastVtkAbstractTransformTimeStamp; }; } // namespace itk #ifndef MITK_MANUAL_INSTANTIATION #include "itkVtkAbstractTransform.txx" #endif #endif /* MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Modules/Core/include/itkVtkAbstractTransform.txx b/Modules/Core/include/itkVtkAbstractTransform.txx index f00836fc51..6e6a87ee5c 100644 --- a/Modules/Core/include/itkVtkAbstractTransform.txx +++ b/Modules/Core/include/itkVtkAbstractTransform.txx @@ -1,236 +1,236 @@ /*============================================================================ 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 "itkVtkAbstractTransform.h" #include #include namespace itk { template itk::VtkAbstractTransform::VtkAbstractTransform() : m_VtkAbstractTransform(nullptr), m_InverseVtkAbstractTransform(nullptr), m_LastVtkAbstractTransformTimeStamp(0) { } template itk::VtkAbstractTransform::~VtkAbstractTransform() { if (m_VtkAbstractTransform != nullptr) m_VtkAbstractTransform->UnRegister(nullptr); } template vtkAbstractTransform *itk::VtkAbstractTransform::GetVtkAbstractTransform() const { return m_VtkAbstractTransform; } template vtkAbstractTransform *itk::VtkAbstractTransform::GetInverseVtkAbstractTransform() const { return m_InverseVtkAbstractTransform; } template void itk::VtkAbstractTransform::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform) { if (m_VtkAbstractTransform == aVtkAbstractTransform) return; if (m_VtkAbstractTransform != nullptr) m_VtkAbstractTransform->UnRegister(nullptr); m_VtkAbstractTransform = aVtkAbstractTransform; if (m_VtkAbstractTransform != nullptr) { m_VtkAbstractTransform->Register(nullptr); m_InverseVtkAbstractTransform = m_VtkAbstractTransform->GetInverse(); // memory managed by m_VtkAbstractTransform } m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime(); this->Modified(); } // Transform a point template typename itk::VtkAbstractTransform::OutputPointType itk::VtkAbstractTransform::TransformPoint(const InputPointType &point) const { assert(m_VtkAbstractTransform != nullptr); OutputPointType outputpoint; vnl_vector vnl_vec; mitk::ScalarType vtkpt[3]; mitk::itk2vtk(point, vtkpt); m_VtkAbstractTransform->TransformPoint(vtkpt, vtkpt); mitk::vtk2itk(vtkpt, outputpoint); return outputpoint; } // Transform a vector template typename itk::VtkAbstractTransform::OutputVectorType itk::VtkAbstractTransform::TransformVector(const InputVectorType &vect) const { assert(m_VtkAbstractTransform != nullptr); OutputVectorType outputvector; vnl_vector vnl_vec; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::vnl2vtk(vect.GetVnlVector(), vtkvec); m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Transform a vnl_vector_fixed template typename itk::VtkAbstractTransform::OutputVnlVectorType itk::VtkAbstractTransform::TransformVector(const InputVnlVectorType &vect) const { assert(m_VtkAbstractTransform != nullptr); OutputVnlVectorType outputvector; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::vnl2vtk(vect, vtkvec); m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Transform a CovariantVector template typename itk::VtkAbstractTransform::OutputCovariantVectorType itk::VtkAbstractTransform::TransformCovariantVector(const InputCovariantVectorType & /*vec*/) const { itkExceptionMacro(<< "implement before using!"); OutputCovariantVectorType result; // Converted vector // for (unsigned int i = 0; i < NDimensions; i++) // { // result[i] = NumericTraits::Zero; // for (unsigned int j = 0; j < NDimensions; j++) // { // result[i] += m_Inverse[j][i]*vec[j]; // Inverse transposed // } // } return result; } // Back transform a point template typename VtkAbstractTransform::InputPointType itk::VtkAbstractTransform::BackTransform( const OutputPointType &point) const { assert(m_VtkAbstractTransform != nullptr); OutputPointType outputpoint; mitk::ScalarType vtkpt[3]; mitk::itk2vtk(point, vtkpt); m_InverseVtkAbstractTransform->TransformPoint(vtkpt, vtkpt); mitk::vtk2itk(vtkpt, outputpoint); return outputpoint; } // Back transform a vector template typename VtkAbstractTransform::InputVectorType itk::VtkAbstractTransform::BackTransform( const OutputVectorType &vect) const { assert(m_VtkAbstractTransform != nullptr); OutputVectorType outputvector; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::itk2vtk(vect, vtkvec); m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Back transform a vnl_vector template typename VtkAbstractTransform::InputVnlVectorType itk::VtkAbstractTransform::BackTransform( const OutputVnlVectorType &vect) const { assert(m_InverseVtkAbstractTransform != nullptr); OutputVnlVectorType outputvector; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::itk2vtk(vect, vtkvec); m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Back Transform a CovariantVector template typename VtkAbstractTransform::InputCovariantVectorType itk::VtkAbstractTransform::BackTransform(const OutputCovariantVectorType &vec) const { itkExceptionMacro(<< "implement before using!"); // for (unsigned int i = 0; i < NDimensions; i++) // { // result[i] = NumericTraits::Zero; // for (unsigned int j = 0; j < NDimensions; j++) // { // result[i] += m_Matrix[j][i]*vec[j]; // Direct matrix transposed // } // } return vec; } template - unsigned long itk::VtkAbstractTransform::GetMTime() const + itk::ModifiedTimeType itk::VtkAbstractTransform::GetMTime() const { if ((m_VtkAbstractTransform != nullptr) && (m_LastVtkAbstractTransformTimeStamp < m_VtkAbstractTransform->GetMTime())) { m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime(); this->Modified(); } return Superclass::GetMTime(); } template void itk::VtkAbstractTransform::SetParameters(const ParametersType &) { // TODO } template void itk::VtkAbstractTransform::SetFixedParameters(const ParametersType &) { // TODO } template void itk::VtkAbstractTransform::ComputeJacobianWithRespectToParameters(const InputPointType &, JacobianType &) const { // TODO } template void itk::VtkAbstractTransform::ComputeJacobianWithRespectToPosition(const InputPointType &, - JacobianType &) const + JacobianPositionType &) const { // TODO } } // namespace itk diff --git a/Modules/Core/include/mitkAbstractTransformGeometry.h b/Modules/Core/include/mitkAbstractTransformGeometry.h index 611fcf7524..17a706ec3a 100644 --- a/Modules/Core/include/mitkAbstractTransformGeometry.h +++ b/Modules/Core/include/mitkAbstractTransformGeometry.h @@ -1,234 +1,234 @@ /*============================================================================ 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 MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include "mitkPlaneGeometry.h" #include #include "itkVtkAbstractTransform.h" class vtkAbstractTransform; namespace mitk { //##Documentation //## @brief Describes a geometry defined by an vtkAbstractTransform and a plane //## //## vtkAbstractTransform is the most general transform in vtk (superclass for //## all vtk geometric transformations). It defines an arbitrary 3D transformation, //## i.e., a transformation of 3D space into 3D space. In contrast, //## AbstractTransformGeometry (since it is a subclass of PlaneGeometry) describes a //## 2D manifold in 3D space. The 2D manifold is defined as the manifold that results //## from transforming a rectangle (given in m_Plane as a PlaneGeometry) by the //## vtkAbstractTransform (given in m_VtkAbstractTransform). //## The PlaneGeometry m_Plane is used to define the parameter space. 2D coordinates are //## first mapped by the PlaneGeometry and the resulting 3D coordinates are put into //## the vtkAbstractTransform. //## @note This class is the superclass of concrete geometries. Since there is no //## write access to the vtkAbstractTransform and m_Plane, this class is somehow //## abstract. For full write access from extern, use ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @sa ExternAbstractTransformGeometry //## @ingroup Geometry class MITKCORE_EXPORT AbstractTransformGeometry : public PlaneGeometry { public: mitkClassMacro(AbstractTransformGeometry, PlaneGeometry); itkFactorylessNewMacro(Self); itkCloneMacro(Self); //##Documentation //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual vtkAbstractTransform *GetVtkAbstractTransform() const; - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; //##Documentation //## @brief Get the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry itkGetConstObjectMacro(Plane, PlaneGeometry); /** * \brief projects the given point onto the curved plane */ bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const override; /** * \brief projects a given vector starting from given point onto the curved plane * \warning no satisfiyng implementation existing yet */ bool Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const override; /** * \brief projects a given vector starting from standard point onto the curved plane * \warning no satisfying implementation existing yet */ bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const override; bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const override; void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const override; bool Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const override; void Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const override; void IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const override; void WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const override; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point2D) is not used. If possible, please use void IndexToWorld(const // mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point2D &atPt2d_units, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const override; //##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::Vector2D &vec_units, mitk::Vector2D &vec_mm) const override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const // mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const override; //##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::Vector2D &vec_mm, mitk::Vector2D &vec_units) const override; bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox = false) const override; virtual mitk::ScalarType GetParametricExtentInMM(int direction) const; virtual const itk::Transform *GetParametricTransform() const; //##Documentation //## @brief Change the parametric bounds to @a oversampling times //## the bounds of m_Plane. //## //## The change is done once (immediately). Later changes of the bounds //## of m_Plane will not influence the parametric bounds. (Consequently, //## there is no method to get the oversampling.) virtual void SetOversampling(mitk::ScalarType oversampling); //##Documentation //## @brief Calculates the standard part of a BaseGeometry //## (IndexToWorldTransform and bounding box) around the //## curved geometry. Has to be implemented in subclasses. //## //## \sa SetFrameGeometry virtual void CalculateFrameGeometry(); //##Documentation //## @brief Set the frame geometry which is used as the standard //## part of an BaseGeometry (IndexToWorldTransform and bounding box) //## //## Maybe used as a hint within which the interpolation shall occur //## by concrete sub-classes. //## \sa CalculateFrameGeometry virtual void SetFrameGeometry(const mitk::BaseGeometry *frameGeometry); itk::LightObject::Pointer InternalClone() const override; //##Documentation //## @brief Get the parametric bounding-box //## //## See AbstractTransformGeometry for an example usage of this. itkGetConstObjectMacro(ParametricBoundingBox, BoundingBox); //##Documentation //## @brief Get the parametric bounds //## //## See AbstractTransformGeometry for an example usage of this. const BoundingBox::BoundsArrayType &GetParametricBounds() const; //##Documentation //## @brief Get the parametric extent //## //## See AbstractTransformGeometry for an example usage of this. mitk::ScalarType GetParametricExtent(int direction) const; protected: AbstractTransformGeometry(); AbstractTransformGeometry(const AbstractTransformGeometry &other); ~AbstractTransformGeometry() override; //##Documentation //## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform) //## //## Protected in this class, made public in ExternAbstractTransformGeometry. virtual void SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform); //##Documentation //## @brief Set the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## ExternAbstractTransformGeometry //## //## Protected in this class, made public in ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @note The PlaneGeometry is cloned, @em not linked/referenced. virtual void SetPlane(const mitk::PlaneGeometry *aPlane); //##Documentation //## @brief The rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry. mitk::PlaneGeometry::Pointer m_Plane; itk::VtkAbstractTransform::Pointer m_ItkVtkAbstractTransform; mitk::BaseGeometry::Pointer m_FrameGeometry; //##Documentation //## @brief Set the parametric bounds //## //## Protected in this class, made public in some sub-classes, e.g., //## ExternAbstractTransformGeometry. virtual void SetParametricBounds(const BoundingBox::BoundsArrayType &bounds); mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; //##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();}; void PreSetSpacing(const mitk::Vector3D &aSpacing) override { Superclass::PreSetSpacing(aSpacing); }; }; } // namespace mitk #endif /* MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/Core/include/mitkAffineTransform3D.h index 8a12d47dd8..57bed6261a 100644 --- a/Modules/Core/include/mitkAffineTransform3D.h +++ b/Modules/Core/include/mitkAffineTransform3D.h @@ -1,24 +1,24 @@ /*============================================================================ 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 MITKAFFINETRANSFORM3D_H_ #define MITKAFFINETRANSFORM3D_H_ -#include +#include #include namespace mitk { - typedef itk::AffineGeometryFrame::TransformType AffineTransform3D; + using AffineTransform3D = itk::ScalableAffineTransform; } #endif /* MITKAFFINETRANSFORM3D_H_ */ diff --git a/Modules/Core/include/mitkBaseData.h b/Modules/Core/include/mitkBaseData.h index c43122796f..14ab1c1856 100644 --- a/Modules/Core/include/mitkBaseData.h +++ b/Modules/Core/include/mitkBaseData.h @@ -1,388 +1,388 @@ /*============================================================================ 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 BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include "mitkOperationActor.h" #include "mitkPropertyList.h" #include "mitkTimeGeometry.h" #include namespace mitk { // class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo //## @remark Some derived classes may support the persistence of the Identifiable UID. //** but it is no guaranteed feature and also depends on the format the data is stored in //** as not all formats support storing of meta information. Please check the documentation //** of the IFileReader and IFileWriter classes to see if the ID-persistance is supported. //** MITK SceneIO supports the UID persistance for all BaseData derived classes. //## @ingroup Data class MITKCORE_EXPORT BaseData : public itk::DataObject, public OperationActor, public Identifiable, public IPropertyOwner { public: mitkClassMacroItkParent(BaseData, itk::DataObject); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ const mitk::TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data as pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ mitk::TimeGeometry *GetTimeGeometry() { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). */ const mitk::TimeGeometry *GetUpdatedTimeGeometry(); /** * \brief Expands the TimeGeometry to a number of TimeSteps. * * The method expands the TimeGeometry to the given number of TimeSteps, * filling newly created elements with empty geometries. Sub-classes should override * this method to handle the elongation of their data vectors, too. * Note that a shrinking is neither possible nor intended. */ virtual void Expand(unsigned int timeSteps); /** * \brief Return the BaseGeometry of the data at time \a t. * * The method does not simply return * m_TimeGeometry->GetGeometry(t). * Before doing this, it makes sure that the BaseGeometry is up-to-date * (by setting the update extent appropriately and calling * UpdateOutputInformation). * * @todo Appropriate setting of the update extent is missing. */ const mitk::BaseGeometry *GetUpdatedGeometry(int t = 0); //##Documentation //## @brief Return the geometry, which is a TimeGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::BaseGeometry *GetGeometry(int t = 0) const { if (m_TimeGeometry.IsNull()) return nullptr; return m_TimeGeometry->GetGeometryForTimeStep(t); } //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation() override; //##Documentation //## @brief Set the RequestedRegion to the LargestPossibleRegion. //## //## This forces a filter to produce all of the output in one execution //## (i.e. not streaming) on the next call to Update(). void SetRequestedRegionToLargestPossibleRegion() override = 0; //##Documentation //## @brief Determine whether the RequestedRegion is outside of the BufferedRegion. //## //## This method returns true if the RequestedRegion //## is outside the BufferedRegion (true if at least one pixel is //## outside). This is used by the pipeline mechanism to determine //## whether a filter needs to re-execute in order to satisfy the //## current request. If the current RequestedRegion is already //## inside the BufferedRegion from the previous execution (and the //## current filter is up to date), then a given filter does not need //## to re-execute bool RequestedRegionIsOutsideOfTheBufferedRegion() override = 0; //##Documentation //## @brief Verify that the RequestedRegion is within the LargestPossibleRegion. //## //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. bool VerifyRequestedRegion() override = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject *data) override; //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmptyTimeStep(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. //## //## This method is implemented in the concrete subclasses of BaseData. void SetRequestedRegion(const itk::DataObject *data) override = 0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation *operation) override; /** * \brief Set the BaseGeometry of the data, which will be referenced (not copied!). * Assumes the data object has only 1 time step ( is a 3D object ) and creates a * new TimeGeometry which saves the given BaseGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedGeometry */ virtual void SetGeometry(BaseGeometry *aGeometry3D); /** * \brief Set the TimeGeometry of the data, which will be referenced (not copied!). * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedTimeGeometry */ virtual void SetTimeGeometry(TimeGeometry *geometry); /** * \brief Set a clone of the provided Geometry as Geometry of the data. * Assumes the data object has only 1 time step ( is a 3D object ) and * creates a new TimeGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * \sa SetGeometry */ virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * * \sa SetGeometry */ virtual void SetClonedTimeGeometry(const TimeGeometry *geometry); //##Documentation //## @brief Set a clone of the provided geometry as BaseGeometry of a given time step. //## //## \sa SetGeometry virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList *propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty *property); //##Documentation //## @brief Convenience method for setting the origin of //## the BaseGeometry instances of all time steps //## //## \warning Geometries contained in the BaseGeometry will //## \em not be changed, e.g. in case the BaseGeometry is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D &origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointer and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointer GetSource() const; //##Documentation //## @brief Get the number of time steps from the TimeGeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); } //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; /** * \sa itk::ProcessObject::Graft */ void Graft(const DataObject *) override; protected: BaseData(); BaseData(const BaseData &other); ~BaseData() override; //##Documentation //## \brief Initialize the TimeGeometry for a number of time steps. //## The TimeGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeGeometry(unsigned int timeSteps = 1); //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry virtual void InitializeEmpty() {} void PrintSelf(std::ostream &os, itk::Indent indent) const override; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable unsigned int m_SourceOutputIndexDuplicate; bool m_Initialized; private: //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeGeometry::Pointer m_TimeGeometry; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index bda3ebf7fc..040e5e8134 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,755 +1,752 @@ /*============================================================================ 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; 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. * * @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 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 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. */ 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 * * 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 = 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. * * @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 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 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. * * 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 = mitk::eps, bool verbose = false); } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkDataNode.h b/Modules/Core/include/mitkDataNode.h index 24f5890c5b..a84844c551 100644 --- a/Modules/Core/include/mitkDataNode.h +++ b/Modules/Core/include/mitkDataNode.h @@ -1,597 +1,597 @@ /*============================================================================ 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 DATATREENODE_H_HEADER_INCLUDED_C1E14338 #define DATATREENODE_H_HEADER_INCLUDED_C1E14338 #include "mitkBaseData.h" //#include "mitkMapper.h" #include "mitkDataInteractor.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include #include #include "mitkColorProperty.h" #include "mitkPropertyList.h" #include "mitkStringProperty.h" //#include "mitkMapper.h" #include "mitkGeometry3D.h" #include "mitkLevelWindow.h" #include #include class vtkLinearTransform; namespace mitk { class BaseRenderer; class Mapper; /** * \brief Class for nodes of the DataTree * * Contains the data (instance of BaseData), a list of mappers, which can * draw the data, a transform (vtkTransform) and a list of properties * (PropertyList). * \ingroup DataManagement * * \todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced * to * bool GetProperty(type&) * * \warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). */ class MITKCORE_EXPORT DataNode : public itk::DataObject, public IPropertyOwner { public: typedef mitk::Geometry3D::Pointer Geometry3DPointer; typedef std::vector> MapperVector; typedef std::map MapOfPropertyLists; typedef std::vector PropertyListKeyNames; typedef std::set GroupTagList; /** * \brief Definition of an itk::Event that is invoked when * a DataInteractor is set on this DataNode. */ itkEventMacro(InteractorChangedEvent, itk::AnyEvent) mitkClassMacroItkParent(DataNode, itk::DataObject); itkFactorylessNewMacro(Self); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; mitk::Mapper *GetMapper(MapperSlotId id) const; /** * \brief Get the data object (instance of BaseData, e.g., an Image) * managed by this DataNode */ BaseData *GetData() const; /** * \brief Get the transformation applied prior to displaying the data as * a vtkTransform * \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead */ vtkLinearTransform *GetVtkTransform(int t = 0) const; /** * \brief Set the data object (instance of BaseData, e.g., an Image) * managed by this DataNode * * Prior set properties are kept if previous data of the node already exists and has the same * type as the new data to be set. Otherwise, the default properties are used. * In case that previous data already exists, the property list of the data node is cleared * before setting new default properties. * * \warning the actor-mode of the vtkInteractor does not work any more, if the transform of the * data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput. */ virtual void SetData(mitk::BaseData *baseData); /** * \brief Set the Interactor. */ virtual void SetDataInteractor(const DataInteractor::Pointer interactor); virtual DataInteractor::Pointer GetDataInteractor() const; mitk::DataNode &operator=(const DataNode &right); mitk::DataNode &operator=(BaseData *right); virtual void SetMapper(MapperSlotId id, mitk::Mapper *mapper); void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; void CopyInformation(const itk::DataObject *data) override; /** * \brief The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string). * * All possible values for the "renderer" parameters of * the diverse GetProperty/List() methods. */ PropertyListKeyNames GetPropertyListNames() const; /** * \brief Set the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value. * * \warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). * * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void SetProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Replace the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference. * * If \a renderer is \a nullptr the property is set in the BaseRenderer-independent * PropertyList of this DataNode. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void ReplaceProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Add the property (instance of BaseProperty) if it does * not exist (or always if\a overwrite is\a true) * with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent * PropertyList). This is set-by-value. * * For\a overwrite ==\a false the property is\em not changed * if it already exists. For\a overwrite ==\a true the method * is identical to SetProperty. * * \sa SetProperty * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void AddProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** * \brief Get the PropertyList of the \a renderer. If \a renderer is \a * nullptr, the BaseRenderer-independent PropertyList of this DataNode * is returned. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::PropertyList *GetPropertyList(const mitk::BaseRenderer *renderer = nullptr) const; mitk::PropertyList *GetPropertyList(const std::string &rendererName) const; /** * \brief Add values from another PropertyList. * * Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). * If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, * set \c replace . * * \param replace true: if \param pList contains a property "visible" of type ColorProperty and our m_PropertyList * also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects * behind the pointer. * * \sa SetProperty * \sa ReplaceProperty * \sa m_PropertyList */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); /** * \brief Get the property (instance of BaseProperty) with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * * If \a fallBackOnDataProperties is true, the data property list is queried as a last resort. * * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::BaseProperty *GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr, bool fallBackOnDataProperties = true) const; /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(itk::SmartPointer &property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property.IsNotNull(); } /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(T *&property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property != nullptr; } /** * \brief Convenience access method for GenericProperty properties * (T being the type of the second parameter) * \return \a true property was found */ template bool GetPropertyValue(const char *propertyKey, T &value, const mitk::BaseRenderer *renderer = nullptr) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey, renderer)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /// \brief Get a set of all group tags from this node's property list GroupTagList GetGroupTags() const; /** * \brief Convenience access method for bool properties (instances of * BoolProperty) * \return \a true property was found */ bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for int properties (instances of * IntProperty) * \return \a true property was found */ bool GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for float properties (instances of * FloatProperty) * \return \a true property was found */ bool GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for double properties (instances of * DoubleProperty) * * If there is no DoubleProperty for the given\c propertyKey argument, the method * looks for a corresponding FloatProperty instance. * * \return \a true property was found */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for string properties (instances of * StringProperty) * \return \a true property was found */ bool GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found */ bool GetColor(float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color") const; /** * \brief Convenience access method for level-window properties (instances of * LevelWindowProperty) * \return \a true property was found */ bool GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow") const; /** * \brief set the node as selected */ void SetSelected(bool selected, const mitk::BaseRenderer *renderer = nullptr); /** * \brief set the node as selected * \return \a true node is selected */ bool IsSelected(const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name") * \return \a true property was found */ bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "name") const { return GetStringProperty(propertyKey, nodeName, renderer); } /** * \brief Extra convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name"). * * This method does not take the renderer specific * propertylists into account, because the name of an object should never be renderer specific. * \returns a std::string with the name of the object (content of "name" Property). * If there is no "name" Property, an empty string will be returned. */ virtual std::string GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } /** Value constant that is used indicate that node names are not set so far.*/ static std::string NO_NAME_VALUE() { return "No Name!"; } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const char *name) { if (name == nullptr) return; this->SetProperty("name", StringProperty::New(name)); } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const std::string name) { this->SetName(name.c_str()); } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty with property-key "visible") * \return \a true property was found * \sa IsVisible */ bool GetVisibility(bool &visible, const mitk::BaseRenderer *renderer, const char *propertyKey = "visible") const { return GetBoolProperty(propertyKey, visible, renderer); } /** * \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found */ bool GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey = "opacity") const; /** * \brief Convenience access method for boolean properties (instances * of BoolProperty). Return value is the value of the property. If the property is * not found, the value of \a defaultIsOn is returned. * * Thus, the return value has a different meaning than in the * GetBoolProperty method! * \sa GetBoolProperty */ bool IsOn(const char *propertyKey, const mitk::BaseRenderer *renderer, bool defaultIsOn = true) const { if (propertyKey == nullptr) return defaultIsOn; GetBoolProperty(propertyKey, defaultIsOn, renderer); return defaultIsOn; } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \sa IsOn */ bool IsVisible(const mitk::BaseRenderer *renderer, const char *propertyKey = "visible", bool defaultIsOn = true) const { return IsOn(propertyKey, renderer, defaultIsOn); } /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(float red, float green, float blue, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting visibility properties (instances * of BoolProperty) * \param visible If set to true, the data will be rendered. If false, the render will skip this data. * \param renderer Specify a renderer if the visibility shall be specific to a renderer * \param propertyKey Can be used to specify a user defined name of the visibility propery. */ void SetVisibility(bool visible, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "visible"); /** * \brief Convenience method for setting opacity properties (instances of * FloatProperty) */ void SetOpacity(float opacity, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "opacity"); /** * \brief Convenience method for setting level-window properties * (instances of LevelWindowProperty) */ void SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow"); /** * \brief Convenience method for setting int properties (instances of * IntProperty) */ void SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting boolean properties (instances of * BoolProperty) */ void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting float properties (instances of * FloatProperty) */ void SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting double properties (instances of * DoubleProperty) */ void SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting string properties (instances of * StringProperty) */ void SetStringProperty(const char *propertyKey, const char *string, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Get the timestamp of the last change of the contents of this node or * the referenced BaseData. */ - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; /** * \brief Get the timestamp of the last change of the reference to the * BaseData. */ unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); } protected: DataNode(); ~DataNode() override; /// Invoked when the property list was modified. Calls Modified() of the DataNode virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event); /// \brief Mapper-slots mutable MapperVector m_Mappers; /** * \brief The data object (instance of BaseData, e.g., an Image) managed * by this DataNode */ BaseData::Pointer m_Data; /** * \brief BaseRenderer-independent PropertyList * * Properties herein can be overwritten specifically for each BaseRenderer * by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. */ PropertyList::Pointer m_PropertyList; /// \brief Map associating each BaseRenderer with its own PropertyList mutable MapOfPropertyLists m_MapOfPropertyLists; DataInteractor::Pointer m_DataInteractor; /// \brief Timestamp of the last change of m_Data itk::TimeStamp m_DataReferenceChangedTime; unsigned long m_PropertyListModifiedObserverTag; }; MITKCORE_EXPORT std::istream &operator>>(std::istream &i, DataNode::Pointer &dtn); MITKCORE_EXPORT std::ostream &operator<<(std::ostream &o, DataNode::Pointer &dtn); } // namespace mitk #endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */ diff --git a/Modules/Core/include/mitkDataStorage.h b/Modules/Core/include/mitkDataStorage.h index a860959317..b884a7b12d 100644 --- a/Modules/Core/include/mitkDataStorage.h +++ b/Modules/Core/include/mitkDataStorage.h @@ -1,442 +1,442 @@ /*============================================================================ 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 MITKDATASTORAGE_H #define MITKDATASTORAGE_H #include "itkObject.h" -#include "itkSimpleFastMutexLock.h" #include "itkVectorContainer.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkMessage.h" #include #include +#include namespace mitk { class NodePredicateBase; class DataNode; class BaseRenderer; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The DataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects, that this object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## If a new node is added to the DataStorage, AddNodeEvent is emitted. //## If a node is removed, RemoveNodeEvent is emitted. //## //## //## \ingroup DataStorage class MITKCORE_EXPORT DataStorage : public itk::Object { public: mitkClassMacroItkParent(DataStorage, itk::Object); //##Documentation //## @brief A Container of objects that is used as a result set of GetSubset() query operations (Set of //SmartPointers // to DataNodes). typedef itk::VectorContainer SetOfObjects; //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the DataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. virtual void Add(DataNode *node, const DataStorage::SetOfObjects *parents = nullptr) = 0; //##Documentation //## @brief Convenience method to add a node that has one parent //## void Add(DataNode *node, DataNode *parent); //##Documentation //## @brief Removes node from the DataStorage //## virtual void Remove(const DataNode *node) = 0; //##Documentation //## @brief Checks if a node exists in the DataStorage //## virtual bool Exists(const DataNode *node) const = 0; //##Documentation //## @brief Removes a set of nodes from the DataStorage //## void Remove(const DataStorage::SetOfObjects *nodes); //##Documentation //## @brief returns a set of data objects that meet the given condition(s) //## //## GetSubset returns a set of objects with a specific data type that meet the condition(s) //## specified in the condition parameter. Conditions can be //## - data type of the data object //## - is source object of specific object (e.g. all source objects of node x) //## - has property with specific value (e.g. OrganType is Liver) //## - negation of any condition //## - conjunction of a set of conditions //## - disjunction of a set of conditions //## Conditions are implemented as predicates using the Composite Design Pattern //## (see definition of NodePredicateBase for details). //## The method returns a set of SmartPointers to the DataNodes that fulfill the //## conditions. A set of all objects can be retrieved with the GetAll() method; SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const; //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## virtual SetOfObjects::ConstPointer GetSources(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const = 0; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. virtual SetOfObjects::ConstPointer GetDerivations(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const = 0; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## virtual SetOfObjects::ConstPointer GetAll() const = 0; //##Documentation //## @brief Convenience method to get the first node that matches the predicate condition //## DataNode *GetNode(const NodePredicateBase *condition = nullptr) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const char *name) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); } //##Documentation //## @brief Convenience method to get the first node with a given name that is derived from sourceNode //## DataNode *GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const; //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const char *name) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedNode(name); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const std::string name) const { return this->GetNamedObject(name.c_str()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name that is derived // from a specific node //## template DataType *GetNamedDerivedObject(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Returns a list of used grouptags //## const DataNode::GroupTagList GetGroupTags() const; /*ITK Mutex */ - mutable itk::SimpleFastMutexLock m_MutexOne; + mutable std::mutex m_MutexOne; /* Public Events */ typedef Message1 DataStorageEvent; //##Documentation //## @brief AddEvent is emitted whenever a new node has been added to the DataStorage. //## //## Observers should register to this event by calling myDataStorage->AddNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->AddNodeEvent.RemoveListener(myObject, //MyObject::MyMethod). //## Note: AddEvents are _not_ emitted if a node is added to DataStorage by adding it to the the underlying //DataTree! // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent AddNodeEvent; //##Documentation //## @brief RemoveEvent is emitted directly before a node is removed from the DataStorage. //## //## Observers should register to this event by calling myDataStorage->RemoveNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->RemoveNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Note: RemoveEvents are also emitted if a node was removed from the DataStorage by deleting it from the //underlying // DataTree // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent RemoveNodeEvent; //##Documentation //## @brief ChangedEvent is emitted directly after a node was changed. //## //## Observers should register to this event by calling myDataStorage->ChangedNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been changed. //## Observers should unregister by calling myDataStorage->ChangedNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::ModifiedEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent ChangedNodeEvent; //##Documentation //## @brief DeleteNodeEvent is emitted directly before a node is deleted. //## //## Observers should register to this event by calling myDataStorage->DeleteNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called when a node is deleted. //## Observers should unregister by calling myDataStorage->DeleteNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::DeleteEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent DeleteNodeEvent; DataStorageEvent InteractorChangedNodeEvent; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the input objects //## //## Throws std::invalid_argument exception if input is nullptr //## @param input set of objects of the DataStorage to be included in the bounding geometry //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the data tree //## (bounding box, minimal spacing of the considered nodes, live-span) //## //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of all visible parts of the //## data tree bounding box, minimal spacing of the considered nodes, live-span) //## //## Simply calls ComputeBoundingGeometry3D(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr); //##Documentation //## @brief Compute the bounding box of data tree structure //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr); //##Documentation //## \brief Compute the bounding box of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## Simply calls ComputeBoundingBox(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. BoundingBox::Pointer ComputeVisibleBoundingBox(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr) { return ComputeBoundingBox("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Compute the time-bounds of the contents of a data tree structure //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeBounds ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2); //##Documentation //## @brief Compute the time-bounds of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## Simply calls ComputeTimeBounds(it, "visible", renderer, boolPropertyKey). //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey TimeBounds ComputeTimeBounds(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeTimeBounds("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Defines whether or not NodeChangedEvent is invoked . //## //## This method can be used to set m_BlockNodeModifiedEvents. //## //## If this flag is true, NodeChangedEvent is not invoked when a //## DataNode is modified. This might be undesired when setting //## many properties on a datanode and you do not want anyone to //## react. void BlockNodeModifiedEvents(bool block); protected: //##Documentation //## @brief EmitAddNodeEvent emits the AddNodeEvent //## //## This method should be called by subclasses to emit the AddNodeEvent void EmitAddNodeEvent(const DataNode *node); //##Documentation //## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent //## //## This method should be called by subclasses to emit the RemoveNodeEvent void EmitRemoveNodeEvent(const DataNode *node); void OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief OnNodeModified listens to modified events of DataNodes. //## //## The node is hidden behind the caller parameter, which has to be casted first. //## If the cast succeeds the ChangedNodeEvent is emitted with this node. void OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief Adds a Modified-Listener to the given Node. void AddListeners(const DataNode *_Node); //##Documentation //## @brief Removes a Modified-Listener from the given Node. void RemoveListeners(const DataNode *_Node); //##Documentation //## @brief Saves Modified-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeModifiedObserverTags; std::map m_NodeInteractorChangedObserverTags; //##Documentation //## @brief Saves Delete-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeDeleteObserverTags; //##Documentation //## @brief If this class changes nodes itself, set this to TRUE in order //## to suppress NodeChangedEvent to be emitted. bool m_BlockNodeModifiedEvents; DataStorage(); ~DataStorage() override; //##Documentation //## @brief Filters a SetOfObjects by the condition. If no condition is provided, the original set is returned SetOfObjects::ConstPointer FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const; //##Documentation //## @brief Prints the contents of the DataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; //##Documentation //## @brief returns the topmost visible node of a given list of nodes. //## The function returns a node that is visible and has the highest layer of a set of given nodes. //## The property list, which is used to find the visibility- and layer-property is is specified by the //## given base renderer. //## MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer* baseRender); } // namespace mitk #endif // MITKDATASTORAGE_H diff --git a/Modules/Core/include/mitkExtractSliceFilter2.h b/Modules/Core/include/mitkExtractSliceFilter2.h index 60303f435c..5366ccbc95 100644 --- a/Modules/Core/include/mitkExtractSliceFilter2.h +++ b/Modules/Core/include/mitkExtractSliceFilter2.h @@ -1,80 +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 mitkExtractSliceFilter2_h #define mitkExtractSliceFilter2_h #include #include #include namespace mitk { /** \brief Extract an arbitrarily oriented 2-d image from a 3-d image. * * Use ExtractSliceFilter2::SetOutputGeometry to specify the orientation of * the 2-d output image. * * If a pixel of the 2-d output image isn't located within the bounds of the * 3-d input image, it is set to the lowest possible pixel value. * * Cubic interpolation is considerably slow on the first update for a newly * set input image. Subsequent filter updates with cubic interpolation are * faster by several orders of magnitude as long as the input image was * neither changed nor modified. * * This filter is completely based on ITK compared to the VTK-based * mitk::ExtractSliceFilter. It is more robust, easy to use, and produces * an mitk::Image with valid geometry. Generally it is not as fast as * mitk::ExtractSliceFilter, though. */ class MITKCORE_EXPORT ExtractSliceFilter2 final : public ImageToImageFilter { public: enum Interpolator { NearestNeighbor, Linear, Cubic }; mitkClassMacro(ExtractSliceFilter2, ImageToImageFilter); itkFactorylessNewMacro(Self); void SetInput(const InputImageType* image) override; void SetInput(unsigned int index, const InputImageType* image) override; const PlaneGeometry* GetOutputGeometry() const; void SetOutputGeometry(PlaneGeometry::Pointer outputGeometry); Interpolator GetInterpolator() const; void SetInterpolator(Interpolator interpolator); private: using Superclass::SetInput; ExtractSliceFilter2(); ~ExtractSliceFilter2() override; void AllocateOutputs() override; // void BeforeThreadedGenerateData() override; // void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override; void GenerateData() override; - void VerifyInputInformation() override; + void VerifyInputInformation() const override; struct Impl; Impl* m_Impl; }; } #endif diff --git a/Modules/Core/include/mitkImage.h b/Modules/Core/include/mitkImage.h index c8b6f03464..a568fb65b8 100644 --- a/Modules/Core/include/mitkImage.h +++ b/Modules/Core/include/mitkImage.h @@ -1,643 +1,643 @@ /*============================================================================ 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 MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include "mitkBaseData.h" #include "mitkImageAccessorBase.h" #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageVtkAccessor.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedData.h" #include #include #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace itk { template class MutexLockHolder; } namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; /** * @brief Image class for storing images * * Can be asked for header information, the data vector, or vtkImageData objects. * If not the complete data is required, the appropriate SubImageSelector class * should be used for access. * Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n * x ND). Channels are for different kind of data, e.g., morphology in * channel 0, velocities in channel 1. All channels must have the same Geometry! In * particular, the dimensions of all channels are the same, only the pixel-type * may differ between channels. * * For importing ITK images use of mitk::ITKImageImport is recommended, see * \ref Adaptor. * * For ITK v3.8 and older: Converting coordinates from the ITK physical * coordinate system (which does not support rotated images) to the MITK world * coordinate system should be performed via the BaseGeometry of the Image, see * BaseGeometry::WorldToItkPhysicalPoint. * * For more information, see \ref MitkImagePage . * @ingroup Data */ class MITKCORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageVtkReadAccessor; friend class ImageVtkWriteAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder *StatisticsHolderPointer; /** This enum is evaluated when setting new data to an image. */ enum ImportMemoryManagementType { CopyMemory, /**< Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. */ ManageMemory, /**< Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. */ ReferenceMemory, /**< Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. */ DontManageMemory = ReferenceMemory }; /** * @brief Vector container of SmartPointers to ImageDataItems; * Class is only for internal usage to allow convenient access to all slices over iterators; * See documentation of ImageDataItem for details. */ typedef std::vector ImageDataItemPointerArray; public: /** * @brief Returns the PixelType of channel @a n. */ const mitk::PixelType GetPixelType(int n = 0) const; /** * @brief Get dimension of the image */ unsigned int GetDimension() const; /** * @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). * * @sa GetDimensions() */ unsigned int GetDimension(int i) const; public: /** * @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. */ virtual vtkImageData *GetVtkImageData(int t = 0, int n = 0); virtual const vtkImageData *GetVtkImageData(int t = 0, int n = 0) const; /** * @brief Check whether slice @a s at time @a t in channel @a n is set */ bool IsSliceSet(int s = 0, int t = 0, int n = 0) const override; /** * @brief Check whether volume at time @a t in channel @a n is set */ bool IsVolumeSet(int t = 0, int n = 0) const override; /** * @brief Check whether the channel @a n is set */ bool IsChannelSet(int n = 0) const override; /** * @brief Set @a data as slice @a s at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a slice (at least is not smaller than a slice), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportSlice with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicSlice, SetImportSlice, SetImportVolume */ virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); /** * @brief Set @a data as volume at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a volume (at least is not smaller than a volume), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportVolume with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicVolume, SetImportVolume */ virtual bool SetVolume(const void *data, int t = 0, int n = 0); /** * @brief Set @a data in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a channel (at least is not smaller than a channel), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportChannel with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicChannel, SetImportChannel */ virtual bool SetChannel(const void *data, int n = 0); /** * @brief Set @a data as slice @a s at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a slice (at least is not smaller than a slice), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicSlice */ virtual bool SetImportSlice( void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @brief Set @a data as volume at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a volume (at least is not smaller than a volume), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicVolume */ virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual bool SetImportVolume(const void *const_data, int t = 0, int n = 0); /** * @brief Set @a data in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a channel (at least is not smaller than a channel), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicChannel */ virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * initialize new (or re-initialize) image information * @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). */ virtual void Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); /** * initialize new (or re-initialize) image information by a BaseGeometry * * \param type * \param geometry * \param channels * @param tDim defines the number of time steps for which the Image should be initialized */ virtual void Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels = 1, int tDim = 1); /** * \brief Initialize new (or re-initialize) image information by a TimeGeometry * * \param type * \param geometry * \param channels * \param tDim override time dimension if the value is bigger than 0 (Default -1) */ virtual void Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels = 1, int tDim = -1); /** * initialize new (or re-initialize) image information by a PlaneGeometry and number of slices * * Initializes the bounding box according to the width/height of the * PlaneGeometry and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. * The spacing is calculated from the PlaneGeometry. * \sa SlicedGeometry3D::InitializeEvenlySpaced */ virtual void Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels = 1, int tDim = 1); /** * initialize new (or re-initialize) image information by another * mitk-image. * Only the header is used, not the data vector! */ virtual void Initialize(const mitk::Image *image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); /** * initialize new (or re-initialize) image information by @a vtkimagedata, * a vtk-image. * Only the header is used, not the data vector! Use * SetVolume(vtkimage->GetScalarPointer()) to set the data vector. * * @param vtkimagedata * @param channels * @param tDim override time dimension in @a vtkimagedata (if >0 and <) * @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) * @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) */ virtual void Initialize(vtkImageData *vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); /** * initialize new (or re-initialize) image information by @a itkimage, * a templated itk-image. * Only the header is used, not the data vector! Use * SetVolume(itkimage->GetBufferPointer()) to set the data vector. * * @param itkimage * @param channels * @param tDim override time dimension in @a itkimage (if >0 and <) * @param sDim override z-space dimension in @a itkimage (if >0 and <) */ template void InitializeByItk(const itkImageType *itkimage, int channels = 1, int tDim = -1, int sDim = -1) { if (itkimage == nullptr) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension = itkimage->GetImageDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = itkimage->GetLargestPossibleRegion().GetSize().GetSize()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } // overwrite number of slices if sDim is set if ((m_Dimension > 2) && (sDim >= 0)) tmpDimensions[2] = sDim; // overwrite number of time points if tDim is set if ((m_Dimension > 3) && (tDim >= 0)) tmpDimensions[3] = tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize( MakePixelType(itkimage->GetNumberOfComponentsPerPixel()), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType &itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = itkspacing[1]; if (m_Dimension >= 3) spacing[2] = itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType &itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = itkorigin[1]; if (m_Dimension >= 3) origin[2] = itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType &itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3 ? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for (j = 0; j < itkDimMax3; ++j) { columnSum = 0.0; for (i = 0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if (columnSum < mitk::eps) { itkdirectionOk = false; } if ((spacing[j] < -mitk::eps) // (normally sized) negative value && (j == 2) && (m_Dimensions[2] == 1)) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j << "]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j << "]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if (itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) if (i == j) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = itkdirection[i][j] * spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = static_cast(GetSlicedGeometry(0)->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); // clean-up delete[] tmpDimensions; this->Initialize(); } /** * @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; /** * @brief Check whether volume at time @a t in channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidVolume(int t = 0, int n = 0) const; /** * @brief Check whether the channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidChannel(int n = 0) const; /** * @brief Returns true if an image is rotated, i.e. its geometry's * transformation matrix has nonzero elements besides the diagonal. * Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. */ bool IsRotated() const; /** * @brief Get the sizes of all dimensions as an integer-array. * * @sa GetDimension(int i); */ unsigned int *GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor(int id = 0) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ void SetGeometry(BaseGeometry *aGeometry3D) override; /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: mitkCloneMacro(Self); - typedef itk::MutexLockHolder MutexHolder; + typedef std::lock_guard MutexHolder; int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; void Expand(unsigned int timeSteps) override; virtual ImageDataItemPointer AllocateSliceData( int s = 0, int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; virtual ImageDataItemPointer AllocateVolumeData( int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; virtual ImageDataItemPointer AllocateChannelData( int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; Image(); Image(const Image &other); ~Image() override; void Clear() override; /** @warning Has to be called by every Initialize method! */ void Initialize() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; - mutable itk::SimpleFastMutexLock m_ImageDataArraysLock; + mutable std::mutex m_ImageDataArraysLock; unsigned int m_Dimension; unsigned int *m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: ImageDataItemPointer GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer GetVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer GetChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const; bool IsSliceSet_unlocked(int s, int t, int n) const; bool IsVolumeSet_unlocked(int t, int n) const; bool IsChannelSet_unlocked(int n) const; /** Stores all existing ImageReadAccessors */ mutable std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ mutable std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ mutable std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ - itk::SimpleFastMutexLock m_ReadWriteLock; + mutable std::mutex m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ - itk::SimpleFastMutexLock m_VtkReadersLock; + mutable std::mutex m_VtkReadersLock; }; /** * @brief Equal A function comparing two images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see * mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKCORE_EXPORT bool Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Modules/Core/include/mitkImageAccessorBase.h b/Modules/Core/include/mitkImageAccessorBase.h index e80978f435..a0419cc23d 100644 --- a/Modules/Core/include/mitkImageAccessorBase.h +++ b/Modules/Core/include/mitkImageAccessorBase.h @@ -1,160 +1,160 @@ /*============================================================================ 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 MITKIMAGEACCESSORBASE_H #define MITKIMAGEACCESSORBASE_H #include #include -#include -#include #include #include "mitkImageDataItem.h" +#include + namespace mitk { //##Documentation //## @brief The ImageAccessorBase class provides a lock mechanism for all inheriting image accessors. //## //## @ingroup Data class Image; /** \brief This struct allows to make ImageAccessors wait for this particular ImageAccessor object*/ struct ImageAccessorWaitLock { /** \brief Holds the number of ImageAccessors, which are waiting until the represented ImageAccessor is released. */ unsigned int m_WaiterCount; /** \brief A mutex that allows other ImageAccessors to wait for the represented ImageAccessor. */ - itk::SimpleFastMutexLock m_Mutex; + std::mutex m_Mutex; }; // Defs to assure dead lock prevention only in case of possible thread handling. #if defined(ITK_USE_SPROC) || defined(ITK_USE_PTHREADS) || defined(ITK_USE_WIN32_THREADS) #define MITK_USE_RECURSIVE_MUTEX_PREVENTION #endif class MITKCORE_EXPORT ImageAccessorBase { friend class Image; friend class ImageReadAccessor; friend class ImageWriteAccessor; template friend class ImagePixelReadAccessor; template friend class ImagePixelWriteAccessor; public: typedef itk::SmartPointer ImageConstPointer; /** \brief defines different flags for the ImageAccessor constructors */ enum Options { /** No specific Options ==> Default */ DefaultBehavior = 0, /** Defines if the Constructor waits for locked memory until it is released or not. If not, an exception is thrown.*/ ExceptionIfLocked = 1, /** Defines if requested Memory has to be coherent. If the parameter is true, it is possible that new Memory has to be allocated to arrange this desired condition. Consequently, this parameter can heavily affect computation time.*/ ForceCoherentMemory = 2, /** Ignores the lock mechanism for immediate access. Only possible with read accessors. */ IgnoreLock = 4 }; virtual ~ImageAccessorBase(); /** \brief Gives const access to the data. */ inline const void *GetData() const { return m_AddressBegin; } protected: // Define type of thread id #ifdef ITK_USE_SPROC typedef int ThreadIDType; #endif #ifdef ITK_USE_WIN32_THREADS typedef DWORD ThreadIDType; #endif #ifdef ITK_USE_PTHREADS typedef pthread_t ThreadIDType; #endif /** \brief Checks validity of given parameters from inheriting classes and stores those parameters in member * variables. */ ImageAccessorBase(ImageConstPointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = DefaultBehavior); /** ImageAccessor has access to the image it belongs to. */ // ImagePointer m_Image; /** Contains a SubRegion (always represented in maximal possible dimension) */ itk::ImageRegion<4> *m_SubRegion; /** Points to the beginning of the image part. */ void *m_AddressBegin; /** Contains the first address after the image part. */ void *m_AddressEnd; /** \brief Stores all extended properties of an ImageAccessor. * The different flags in mitk::ImageAccessorBase::Options can be unified by bitwise operations. */ int m_Options; /** Defines if the accessed image part lies coherently in memory */ bool m_CoherentMemory; /** \brief Pointer to a WaitLock struct, that allows other ImageAccessors to wait for this ImageAccessor */ ImageAccessorWaitLock *m_WaitLock; /** \brief Increments m_WaiterCount. A call of this method is prohibited unless the Mutex m_ReadWriteLock in the * mitk::Image class is Locked. */ inline void Increment() { m_WaitLock->m_WaiterCount += 1; } /** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor * object * \throws mitk::Exception if memory area is incoherent (not supported yet) */ bool Overlap(const ImageAccessorBase *iAB); /** \brief Uses the WaitLock to wait for another ImageAccessor*/ void WaitForReleaseOf(ImageAccessorWaitLock *wL); ThreadIDType m_Thread; /** \brief Prevents a recursive mutex lock by comparing thread ids of competing image accessors */ void PreventRecursiveMutexLock(ImageAccessorBase *iAB); virtual const Image *GetImage() const = 0; private: /** \brief System dependend thread method, to prevent recursive mutex access */ ThreadIDType CurrentThreadHandle(); /** \brief System dependend thread method, to prevent recursive mutex access */ inline bool CompareThreadHandles(ThreadIDType, ThreadIDType); }; class MemoryIsLockedException : public Exception { public: mitkExceptionClassMacro(MemoryIsLockedException, Exception) }; } #endif diff --git a/Modules/Core/include/mitkImageGenerator.h b/Modules/Core/include/mitkImageGenerator.h index 5642b6355b..fee6d12c9d 100644 --- a/Modules/Core/include/mitkImageGenerator.h +++ b/Modules/Core/include/mitkImageGenerator.h @@ -1,236 +1,236 @@ /*============================================================================ 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 ImageGenerator_H_HEADER_INCLUDED #define ImageGenerator_H_HEADER_INCLUDED #include "mitkImageWriteAccessor.h" #include #include #include #include namespace mitk { /** * @brief generator for synthetic MITK images * This is a helper class to generate synthetic MITK images (random or gradient). * * @ingroup IO */ class MITKCORE_EXPORT ImageGenerator { public: /** * \brief Generates gradient image with the defined size and spacing */ template static mitk::Image::Pointer GenerateGradientImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ, float spacingX = 1, float spacingY = 1, float spacingZ = 1) { typedef itk::Image ImageType; typename ImageType::RegionType imageRegion; imageRegion.SetSize(0, dimX); imageRegion.SetSize(1, dimY); imageRegion.SetSize(2, dimZ); typename ImageType::SpacingType spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; mitk::Point3D origin; origin.Fill(0.0); itk::Matrix directionMatrix; directionMatrix.SetIdentity(); typename ImageType::Pointer image = ImageType::New(); image->SetSpacing(spacing); image->SetOrigin(origin); image->SetDirection(directionMatrix); image->SetLargestPossibleRegion(imageRegion); image->SetBufferedRegion(imageRegion); image->SetRequestedRegion(imageRegion); image->Allocate(); image->FillBuffer(0.0); typedef itk::ImageRegionIterator IteratorOutputType; IteratorOutputType it(image, imageRegion); it.GoToBegin(); TPixelType val = 0; while (!it.IsAtEnd()) { it.Set(val); val++; ++it; } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk(image.GetPointer()); mitkImage->SetVolume(image->GetBufferPointer()); return mitkImage; } /** * \brief Generates an image of a same geometry as the one given as reference The image buffer is filled to the fill_value given as parameter */ template static mitk::Image::Pointer GenerateImageFromReference(mitk::Image::Pointer reference, TPixelType fill_value) { mitk::Image::Pointer output = mitk::Image::New(); mitk::PixelType output_type = MakeScalarPixelType(); // all metadata (except type) come from reference image output->SetGeometry(reference->GetGeometry()); output->Initialize(output_type, reference->GetDimension(), reference->GetDimensions()); // get a pointer to the image buffer to write into TPixelType *imageBuffer = nullptr; try { mitk::ImageWriteAccessor writeAccess(output); imageBuffer = static_cast(writeAccess.GetData()); } catch (...) { MITK_ERROR << "Write access not granted on mitk::Image."; } // fill the buffer with the specifed value for (unsigned int i = 0; i < output->GetVolumeData(0)->GetSize(); i++) { imageBuffer[i] = fill_value; } return output; } /*! \brief Generates random image with the defined size and spacing */ template static mitk::Image::Pointer GenerateRandomImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ = 1, unsigned int dimT = 1, mitk::ScalarType spacingX = 1, mitk::ScalarType spacingY = 1, mitk::ScalarType spacingZ = 1, const double randomMax = 1000.0f, const double randMin = 0.0f) { // set the data type according to the template mitk::PixelType type = MakeScalarPixelType(); // type.Initialize(typeid(TPixelType)); // initialize the MITK image with given dimenion and data type mitk::Image::Pointer output = mitk::Image::New(); auto dimensions = new unsigned int[4]; unsigned int numberOfDimensions = 0; unsigned int bufferSize = 0; // check which dimension is needed if (dimT <= 1) { if (dimZ <= 1) { // 2D numberOfDimensions = 2; dimensions[0] = dimX; dimensions[1] = dimY; bufferSize = dimX * dimY; } else { // 3D numberOfDimensions = 3; dimensions[0] = dimX; dimensions[1] = dimY; dimensions[2] = dimZ; bufferSize = dimX * dimY * dimZ; } } else { // 4D numberOfDimensions = 4; dimensions[0] = dimX; dimensions[1] = dimY; dimensions[2] = dimZ; dimensions[3] = dimT; bufferSize = dimX * dimY * dimZ * dimT; } output->Initialize(type, numberOfDimensions, dimensions); mitk::Vector3D spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; output->SetSpacing(spacing); // get a pointer to the image buffer to write into TPixelType *imageBuffer = nullptr; try { mitk::ImageWriteAccessor writeAccess(output); imageBuffer = static_cast(writeAccess.GetData()); } catch (...) { MITK_ERROR << "Write access not granted on mitk::Image."; } // initialize the random generator itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize(); // fill the buffer for each pixel/voxel for (unsigned int i = 0; i < bufferSize; i++) { // the comparison of the component type is sufficient enough since the mitk::PixelType type object is // created as SCALAR and hence does not need the comparison against type.GetPixelTypeId() == - // itk::ImageIOBase::SCALAR - if (type.GetComponentType() == itk::ImageIOBase::INT) // call integer function + // itk::IOPixelEnum::SCALAR + if (type.GetComponentType() == itk::IOComponentEnum::INT) // call integer function { imageBuffer[i] = (TPixelType)randomGenerator->GetIntegerVariate((int)randomMax); // TODO random generator does not support integer values in a given range (e.g. from 5-10) // range is always [0, (int)randomMax] } - else if ((type.GetComponentType() == itk::ImageIOBase::DOUBLE) || - (type.GetComponentType() == itk::ImageIOBase::FLOAT)) // call floating point function + else if ((type.GetComponentType() == itk::IOComponentEnum::DOUBLE) || + (type.GetComponentType() == itk::IOComponentEnum::FLOAT)) // call floating point function { imageBuffer[i] = (TPixelType)randomGenerator->GetUniformVariate(randMin, randomMax); } - else if (type.GetComponentType() == itk::ImageIOBase::UCHAR) + else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR) { // use the integer randomGenerator with mod 256 to generate unsigned char values imageBuffer[i] = (unsigned char)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 256; } - else if (type.GetComponentType() == itk::ImageIOBase::USHORT) + else if (type.GetComponentType() == itk::IOComponentEnum::USHORT) { imageBuffer[i] = (unsigned short)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 65536; } else { MITK_ERROR << "Datatype not supported yet."; // TODO call different methods for other datatypes } } return output; } }; } // namespace mitk #endif /* ImageGenerator_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkImageSource.h b/Modules/Core/include/mitkImageSource.h index d5a26274e8..52461c8159 100644 --- a/Modules/Core/include/mitkImageSource.h +++ b/Modules/Core/include/mitkImageSource.h @@ -1,251 +1,251 @@ /*============================================================================ 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 IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC #define IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC #include "mitkBaseDataSource.h" #include "mitkImage.h" #include namespace mitk { /** * @brief Superclass of all classes generating Images (instances of class * Image) as output. * * In itk and vtk the generated result of a ProcessObject is only guaranteed * to be up-to-date, when Update() of the ProcessObject or the generated * DataObject is called immediately before access of the data stored in the * DataObject. This is also true for subclasses of mitk::BaseProcess and thus * for mitk::ImageSource. But there are also three access methods provided * that guarantee an up-to-date result (by first calling Update and then * returning the result of GetOutput()): GetData(), GetPic() and * GetVtkImageData(). * @ingroup Process */ class MITKCORE_EXPORT ImageSource : public BaseDataSource { public: mitkClassMacro(ImageSource, BaseDataSource); /** @brief Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** @brief Some convenient typedefs. */ typedef mitk::Image OutputImageType; typedef OutputImageType OutputType; typedef OutputImageType::Pointer OutputImagePointer; typedef SlicedData::RegionType OutputImageRegionType; /** * @brief Get the output data of this image source object. * * The output of this * function is not valid until an appropriate Update() method has * been called, either explicitly or implicitly. Both the filter * itself and the data object have Update() methods, and both * methods update the data. Here are three ways to use * GetOutput() and make sure the data is valid. In these * examples, \a image is a pointer to some Image object, and the * particular ProcessObjects involved are filters. The same * examples apply to non-image (e.g. Mesh) data as well. * * \code * anotherFilter->SetInput( someFilter->GetOutput() ); * anotherFilter->Update(); * \endcode * * In this situation, \a someFilter and \a anotherFilter are said * to constitute a \b pipeline. * * \code * image = someFilter->GetOutput(); * image->Update(); * \endcode * * \code * someFilter->Update(); * image = someFilter->GetOutput(); * \endcode * (In the above example, the two lines of code can be in * either order.) * * Note that Update() is not called automatically except within a * pipeline as in the first example. When \b streaming (using a * StreamingImageFilter) is activated, it may be more efficient to * use a pipeline than to call Update() once for each filter in * turn. * * For an image, the data generated is for the requested * Region, which can be set using ImageBase::SetRequestedRegion(). * By default, the largest possible region is requested. * * For Filters which have multiple outputs of different types, the * GetOutput() method assumes the output is of OutputImageType. For * the GetOutput(DataObjectPointerArraySizeType) method, a dynamic_cast is performed * incase the filter has outputs of different types or image * types. Derived classes should have named get methods for these * outputs. */ mitkBaseDataSourceGetOutputDeclarations /** @brief Make a DataObject of the correct type to used as the specified * output. * * Every ProcessObject subclass must be able to create a * DataObject that can be used as a specified output. This method * is automatically called when DataObject::DisconnectPipeline() is * called. DataObject::DisconnectPipeline, disconnects a data object * from being an output of its current source. When the data object * is disconnected, the ProcessObject needs to construct a replacement * output data object so that the ProcessObject is in a valid state. * So DataObject::DisconnectPipeline eventually calls * ProcessObject::MakeOutput. Note that MakeOutput always returns a * SmartPointer to a DataObject. If a subclass of ImageSource has * multiple outputs of different types, then that class must provide * an implementation of MakeOutput(). */ itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override; /** * This is a default implementation to make sure we have something. * Once all the subclasses of ProcessObject provide an appopriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; virtual vtkImageData *GetVtkImageData(); virtual const vtkImageData *GetVtkImageData() const; protected: ImageSource(); ~ImageSource() override {} /** @brief A version of GenerateData() specific for image processing * filters. * * This implementation will split the processing across * multiple threads. The buffer is allocated by this method. Then * the BeforeThreadedGenerateData() method is called (if * provided). Then, a series of threads are spawned each calling * ThreadedGenerateData(). After all the threads have completed * processing, the AfterThreadedGenerateData() method is called (if * provided). If an image processing filter cannot be threaded, the * filter should provide an implementation of GenerateData(). That * implementation is responsible for allocating the output buffer. * If a filter an be threaded, it should NOT provide a * GenerateData() method but should provide a ThreadedGenerateData() * instead. * * \sa ThreadedGenerateData() */ void GenerateData() override; /** @brief If an imaging filter can be implemented as a multithreaded * algorithm, the filter will provide an implementation of * ThreadedGenerateData(). * * This superclass will automatically split * the output image into a number of pieces, spawn multiple threads, * and call ThreadedGenerateData() in each thread. Prior to spawning * threads, the BeforeThreadedGenerateData() method is called. After * all the threads have completed, the AfterThreadedGenerateData() * method is called. If an image processing filter cannot support * threading, that filter should provide an implementation of the * GenerateData() method instead of providing an implementation of * ThreadedGenerateData(). If a filter provides a GenerateData() * method as its implementation, then the filter is responsible for * allocating the output data. If a filter provides a * ThreadedGenerateData() method as its implementation, then the * output memory will allocated automatically by this superclass. * The ThreadedGenerateData() method should only produce the output * specified by "outputThreadRegion" * parameter. ThreadedGenerateData() cannot write to any other * portion of the output image (as this is responsibility of a * different thread). * * \sa GenerateData(), SplitRequestedRegion() */ virtual void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId); /** @brief This method is intentionally left blank. * * ImageSource's need not * Initialize their containers. The Image::Allocate() method (called * from GenerateData()) will resize the container if more memory is * needed. Otherwise, the memory can be reused. */ void PrepareOutputs() override; /** @brief The GenerateData method normally allocates the buffers for all of the * outputs of a filter. * * Some filters may want to override this default * behavior. For example, a filter may have multiple outputs with * varying resolution. Or a filter may want to process data in place by * grafting its input to its output.*/ virtual void AllocateOutputs(); /** @brief If an imaging filter needs to perform processing after the buffer * has been allocated but before threads are spawned, the filter can * can provide an implementation for BeforeThreadedGenerateData(). * * The execution flow in the default GenerateData() method will be: * 1) Allocate the output buffer * 2) Call BeforeThreadedGenerateData() * 3) Spawn threads, calling ThreadedGenerateData() in each thread. * 4) Call AfterThreadedGenerateData() * Note that this flow of control is only available if a filter provides * a ThreadedGenerateData() method and NOT a GenerateData() method. */ virtual void BeforeThreadedGenerateData() {} /** @brief If an imaging filter needs to perform processing after all * processing threads have completed, the filter can can provide an * implementation for AfterThreadedGenerateData(). * * The execution * flow in the default GenerateData() method will be: * 1) Allocate the output buffer * 2) Call BeforeThreadedGenerateData() * 3) Spawn threads, calling ThreadedGenerateData() in each thread. * 4) Call AfterThreadedGenerateData() * Note that this flow of control is only available if a filter provides * a ThreadedGenerateData() method and NOT a GenerateData() method. */ virtual void AfterThreadedGenerateData() {} /** @brief Split the output's RequestedRegion into "num" pieces, returning * region "i" as "splitRegion". * * This method is called "num" times. The * regions must not overlap. The method returns the number of pieces that * the routine is capable of splitting the output RequestedRegion, * i.e. return value is less than or equal to "num". */ virtual unsigned int SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion); /** @brief Static function used as a "callback" by the MultiThreader. * * The threading library will call this routine for each thread, which will delegate the * control to ThreadedGenerateData(). */ - static ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg); + static itk::ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg); /** @brief Internal structure used for passing image data into the threading library */ struct ThreadStruct { Pointer Filter; }; private: ImageSource(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // namespace mitk #endif /* IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC */ diff --git a/Modules/Core/include/mitkImageToItk.txx b/Modules/Core/include/mitkImageToItk.txx index dea853f238..29abe89a77 100644 --- a/Modules/Core/include/mitkImageToItk.txx +++ b/Modules/Core/include/mitkImageToItk.txx @@ -1,290 +1,290 @@ /*============================================================================ 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 IMAGETOITK_TXX_INCLUDED_C1C2FCD2 #define IMAGETOITK_TXX_INCLUDED_C1C2FCD2 #include "itkImportMitkImageContainer.h" #include "mitkBaseProcess.h" #include "mitkException.h" #include "mitkImageReadAccessor.h" #include "mitkImageToItk.h" #include "mitkImageWriteAccessor.h" #include template struct SetLengthHelper { SetLengthHelper(TImageType *in) { m_Image = in; } private: TImageType *m_Image; }; template struct SetLengthHelper> { typedef itk::Image TImageType; SetLengthHelper(TImageType *in) { m_Image = in; } void SetVectorLength(size_t) {} private: TImageType *m_Image; }; template struct SetLengthHelper> { typedef itk::VectorImage TImageType; SetLengthHelper(TImageType *in) { m_Image = in; } void SetVectorLength(size_t len) { m_Image->SetVectorLength(len); } private: TImageType *m_Image; }; template void mitk::ImageToItk::SetInput(mitk::Image *input) { this->SetInput(static_cast(input)); m_ConstInput = false; } template void mitk::ImageToItk::SetInput(const mitk::Image *input) { this->CheckInput(input); // Process object is not const-correct so the const_cast is required here itk::ProcessObject::PushFrontInput(input); m_ConstInput = true; } template mitk::Image *mitk::ImageToItk::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(itk::ProcessObject::GetInput(0)); } template const mitk::Image *mitk::ImageToItk::GetInput() const { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(itk::ProcessObject::GetInput(0)); } template void mitk::ImageToItk::GenerateData() { // Allocate output mitk::Image::Pointer input = this->GetInput(); typename Superclass::OutputImageType::Pointer output = this->GetOutput(); unsigned long noBytes = input->GetDimension(0); for (unsigned int i = 1; i < TOutputImage::GetImageDimension(); ++i) { noBytes = noBytes * input->GetDimension(i); } const mitk::PixelType pixelType = input->GetPixelType(); - if (pixelType.GetPixelType() == itk::ImageIOBase::VECTOR) + if (pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR) { noBytes *= pixelType.GetNumberOfComponents(); SetLengthHelper helper(output.GetPointer()); helper.SetVectorLength(pixelType.GetNumberOfComponents()); } std::unique_ptr imageAccess; if (m_ConstInput) { imageAccess.reset(new mitk::ImageReadAccessor(input, nullptr, m_Options)); } else { imageAccess.reset(new mitk::ImageWriteAccessor(input, nullptr, m_Options)); } // hier wird momentan wohl nur der erste Channel verwendet??!! if (imageAccess->GetData() == nullptr) { itkWarningMacro(<< "no image data to import in ITK image"); RegionType bufferedRegion; output->SetBufferedRegion(bufferedRegion); return; } if (m_CopyMemFlag) { itkDebugMacro("copyMem ..."); output->Allocate(); memcpy(output->GetBufferPointer(), imageAccess->GetData(), sizeof(InternalPixelType) * noBytes); } else { itkDebugMacro("do not copyMem ..."); typedef itk::ImportMitkImageContainer ImportContainerType; typename ImportContainerType::Pointer import; import = ImportContainerType::New(); import->Initialize(); itkDebugMacro(<< "size of container = " << import->Size()); // import->SetImageDataItem(m_ImageDataItem); import->SetImageAccessor(imageAccess.release(), sizeof(InternalPixelType) * noBytes); output->SetPixelContainer(import); itkDebugMacro(<< "size of container = " << import->Size()); } } template void mitk::ImageToItk::UpdateOutputInformation() { mitk::Image::Pointer input = this->GetInput(); if (input.IsNotNull() && (input->GetSource().IsNotNull()) && input->GetSource()->Updating()) { typename Superclass::OutputImageType::Pointer output = this->GetOutput(); unsigned long t1 = input->GetUpdateMTime() + 1; if (t1 > this->m_OutputInformationMTime.GetMTime()) { output->SetPipelineMTime(t1); this->GenerateOutputInformation(); this->m_OutputInformationMTime.Modified(); } return; } Superclass::UpdateOutputInformation(); } template void mitk::ImageToItk::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); typename Superclass::OutputImageType::Pointer output = this->GetOutput(); // allocate size, origin, spacing, direction in types of output image SizeType size; const unsigned int itkDimMin3 = (TOutputImage::ImageDimension > 3 ? TOutputImage::ImageDimension : 3); const unsigned int itkDimMax3 = (TOutputImage::ImageDimension < 3 ? TOutputImage::ImageDimension : 3); typename Superclass::OutputImageType::PointType::ValueType origin[itkDimMin3]; typename Superclass::OutputImageType::SpacingType::ComponentType spacing[itkDimMin3]; typename Superclass::OutputImageType::DirectionType direction; // copy as much information as possible into size and spacing unsigned int i; for (i = 0; i < itkDimMax3; ++i) { size[i] = input->GetDimension(i); spacing[i] = input->GetGeometry()->GetSpacing()[i]; } for (; i < TOutputImage::ImageDimension; ++i) { origin[i] = 0.0; size[i] = input->GetDimension(i); spacing[i] = 1.0; } // build region from size IndexType start; start.Fill(0); RegionType region; region.SetIndex(start); region.SetSize(size); // copy as much information as possible into origin const mitk::Point3D &mitkorigin = input->GetGeometry()->GetOrigin(); itk2vtk(mitkorigin, origin); // copy as much information as possible into direction direction.SetIdentity(); unsigned int j; const AffineTransform3D::MatrixType &matrix = input->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); /// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix. /// If it is only a rotation around the axial plane normal, it can be express with a 2x2 matrix. /// In this case, the ITK image conservs this information and is identical to the MITK image! /// If the MITK image contains any other rotation, the ITK image will have no rotation at all. /// Spacing is of course conserved in both cases. // the following loop devides by spacing now to normalize columns. // counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092. // Check if information is lost if (TOutputImage::ImageDimension <= 2) { if ((TOutputImage::ImageDimension == 2) && ((matrix[0][2] != 0) || (matrix[1][2] != 0) || (matrix[2][0] != 0) || (matrix[2][1] != 0) || ((matrix[2][2] != 1) && (matrix[2][2] != -1)))) { // The 2D MITK image contains 3D rotation information. // This cannot be expressed in a 2D ITK image, so the ITK image will have no rotation } else { // The 2D MITK image can be converted to an 2D ITK image without information loss! for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) direction[i][j] = matrix[i][j] / spacing[j]; } } else { // Normal 3D image. Conversion possible without problem! for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) direction[i][j] = matrix[i][j] / spacing[j]; } // set information into output image output->SetRegions(region); output->SetOrigin(origin); output->SetSpacing(spacing); output->SetDirection(direction); } template void mitk::ImageToItk::CheckInput(const mitk::Image *input) const { if (input == nullptr) { itkExceptionMacro(<< "image is null"); } if (input->GetDimension() != TOutputImage::GetImageDimension()) { itkExceptionMacro(<< "image has dimension " << input->GetDimension() << " instead of " << TOutputImage::GetImageDimension()); } if (!(input->GetPixelType() == mitk::MakePixelType(input->GetPixelType().GetNumberOfComponents()))) { itkExceptionMacro(<< "image has wrong pixel type "); } } template void mitk::ImageToItk::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } #endif // IMAGETOITK_TXX_INCLUDED_C1C2FCD2 diff --git a/Modules/Core/include/mitkMessage.h b/Modules/Core/include/mitkMessage.h index 47cf00cac8..564472316c 100644 --- a/Modules/Core/include/mitkMessage.h +++ b/Modules/Core/include/mitkMessage.h @@ -1,697 +1,697 @@ /*============================================================================ 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 mitkMessageHIncluded #define mitkMessageHIncluded #include -#include +#include #include /** * Adds a Message<> variable and methods to add/remove message delegates to/from * this variable. */ #define mitkNewMessageMacro(msgHandleObject) \ private: \ ::mitk::Message<> m_##msgHandleObject##Message; \ \ public: \ inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessageWithReturnMacro(msgHandleObject, returnType) \ private: \ ::mitk::Message m_##msgHandleObject##Message; \ \ public: \ inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessage1Macro(msgHandleObject, type1) \ private: \ ::mitk::Message1 m_##msgHandleObject##Message; \ \ public: \ void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1 &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1 &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessage2Macro(msgHandleObject, type1, type2) \ private: \ ::mitk::Message2 m_##msgHandleObject##Message; \ \ public: \ void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2 &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2 &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } namespace mitk { template class MessageAbstractDelegate { public: virtual ~MessageAbstractDelegate() {} virtual A Execute() const = 0; virtual bool operator==(const MessageAbstractDelegate *cmd) const = 0; virtual MessageAbstractDelegate *Clone() const = 0; }; template class MessageAbstractDelegate1 { public: virtual ~MessageAbstractDelegate1() {} virtual A Execute(T t) const = 0; virtual bool operator==(const MessageAbstractDelegate1 *cmd) const = 0; virtual MessageAbstractDelegate1 *Clone() const = 0; }; template class MessageAbstractDelegate2 { public: virtual ~MessageAbstractDelegate2() {} virtual A Execute(T t, U u) const = 0; virtual bool operator==(const MessageAbstractDelegate2 *cmd) const = 0; virtual MessageAbstractDelegate2 *Clone() const = 0; }; template class MessageAbstractDelegate3 { public: virtual ~MessageAbstractDelegate3() {} virtual A Execute(T t, U u, V v) const = 0; virtual bool operator==(const MessageAbstractDelegate3 *cmd) const = 0; virtual MessageAbstractDelegate3 *Clone() const = 0; }; template class MessageAbstractDelegate4 { public: virtual ~MessageAbstractDelegate4() {} virtual A Execute(T t, U u, V v, W w) const = 0; virtual bool operator==(const MessageAbstractDelegate4 *cmd) const = 0; virtual MessageAbstractDelegate4 *Clone() const = 0; }; /** * This class essentially wraps a function pointer with signature * A(R::*function)(). A is the return type of your callback function * and R the type of the class implementing the function. * * Use this class to add a callback function to * messages without parameters. */ template class MessageDelegate : public MessageAbstractDelegate { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate(R *object, A (R::*memberFunctionPointer)()) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate() override {} // override function "Call" A Execute() const override { return (m_Object->*m_MemberFunctionPointer)(); // execute member function } bool operator==(const MessageAbstractDelegate *c) const override { const MessageDelegate *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate *Clone() const override { return new MessageDelegate(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(); // pointer to member function }; /** * This class essentially wraps a function pointer with signature * A(R::*function)(T). A is the return type of your callback function, * R the type of the class implementing the function and T the type * of the argument. * * Use this class to add a callback function to * messages with one parameter. * * If you need more parameters, use MessageDelegate2 etc. */ template class MessageDelegate1 : public MessageAbstractDelegate1 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate1(R *object, A (R::*memberFunctionPointer)(T)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate1() override {} // override function "Call" A Execute(T t) const override { return (m_Object->*m_MemberFunctionPointer)(t); // execute member function } bool operator==(const MessageAbstractDelegate1 *c) const override { const MessageDelegate1 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate1 *Clone() const override { return new MessageDelegate1(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T); // pointer to member function }; template class MessageDelegate2 : public MessageAbstractDelegate2 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate2(R *object, A (R::*memberFunctionPointer)(T, U)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate2() override {} // override function "Call" A Execute(T t, U u) const override { return (m_Object->*m_MemberFunctionPointer)(t, u); // execute member function } bool operator==(const MessageAbstractDelegate2 *c) const override { const MessageDelegate2 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate2 *Clone() const override { return new MessageDelegate2(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U); // pointer to member function }; template class MessageDelegate3 : public MessageAbstractDelegate3 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate3(R *object, A (R::*memberFunctionPointer)(T, U, V)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate3() override {} // override function "Call" A Execute(T t, U u, V v) const override { return (m_Object->*m_MemberFunctionPointer)(t, u, v); // execute member function } bool operator==(const MessageAbstractDelegate3 *c) const override { const MessageDelegate3 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate3 *Clone() const override { return new MessageDelegate3(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U, V); // pointer to member function }; template class MessageDelegate4 : public MessageAbstractDelegate4 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate4(R *object, A (R::*memberFunctionPointer)(T, U, V, W)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~MessageDelegate4() {} // override function "Call" virtual A Execute(T t, U u, V v, W w) const { return (m_Object->*m_MemberFunctionPointer)(t, u, v, w); // execute member function } bool operator==(const MessageAbstractDelegate4 *c) const { const MessageDelegate4 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate4 *Clone() const { return new MessageDelegate4(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U, V, W); // pointer to member function }; template class MessageBase { public: typedef std::vector ListenerList; virtual ~MessageBase() { for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { delete *iter; } } MessageBase() {} MessageBase(const MessageBase &o) { for (typename ListenerList::iterator iter = o.m_Listeners.begin(); iter != o.m_Listeners.end(); ++iter) { m_Listeners.push_back((*iter)->Clone()); } } MessageBase &operator=(const MessageBase &o) { MessageBase tmp(o); std::swap(tmp.m_Listeners, this->m_Listeners); return *this; } void AddListener(const AbstractDelegate &delegate) const { AbstractDelegate *msgCmd = delegate.Clone(); - m_Mutex.Lock(); + m_Mutex.lock(); for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { if ((*iter)->operator==(msgCmd)) { delete msgCmd; - m_Mutex.Unlock(); + m_Mutex.unlock(); return; } } m_Listeners.push_back(msgCmd); - m_Mutex.Unlock(); + m_Mutex.unlock(); } void operator+=(const AbstractDelegate &delegate) const { this->AddListener(delegate); } void RemoveListener(const AbstractDelegate &delegate) const { - m_Mutex.Lock(); + m_Mutex.lock(); for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { if ((*iter)->operator==(&delegate)) { delete *iter; m_Listeners.erase(iter); - m_Mutex.Unlock(); + m_Mutex.unlock(); return; } } - m_Mutex.Unlock(); + m_Mutex.unlock(); } void operator-=(const AbstractDelegate &delegate) const { this->RemoveListener(delegate); } const ListenerList &GetListeners() const { return m_Listeners; } bool HasListeners() const { return !m_Listeners.empty(); } bool IsEmpty() const { return m_Listeners.empty(); } protected: /** * \brief List of listeners. * * This is declared mutable for a reason: Imagine an object that sends out notifications and * someone gets a const Database object, because he/she should not write to the * database. He/she should anyway be able to register for notifications about changes in the database * -- this is why AddListener and RemoveListener are declared const. m_Listeners must be * mutable so that AddListener and RemoveListener can modify it regardless of the object's constness. */ mutable ListenerList m_Listeners; - mutable itk::SimpleFastMutexLock m_Mutex; + mutable std::mutex m_Mutex; }; /** * \brief Event/message/notification class. * * \sa mitk::BinaryThresholdTool * \sa QmitkBinaryThresholdToolGUI * * This totally ITK, Qt, VTK, whatever toolkit independent class * allows one class to send out messages and another class to * receive these message. This class is templated over the * return type (A) of the callback functions. * There are variations of this class * (Message1, Message2, etc.) for sending * one, two or more parameters along with the messages. * * This is an implementation of the Observer pattern. * * \li There is no guarantee about the order of which observer is notified first. At the moment the observers which * register first will be notified first. * \li Notifications are synchronous, by direct method calls. There is no support for asynchronous messages. * * To conveniently add methods for registering/unregistering observers * to Message variables of your class, you can use the mitkNewMessageMacro * macros. */ template class Message : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send() const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(); } } void operator()() const { this->Send(); } }; // message with 1 parameter and return type template class Message1 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t); } } void operator() (T t) const { this->Send(t); } }; // message with 2 parameters and return type template class Message2 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u); } } void operator()(T t, U u) const { this->Send(t, u); } }; // message with 3 parameters and return type template class Message3 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u, V v) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u, v); } } void operator()(T t, U u, V v) const { this->Send(t, u, v); } }; // message with 4 parameters and return type template class Message4 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u, V v, W w) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u, v, w); } } void operator()(T t, U u, V v, W w) const { this->Send(t, u, v, w); } }; /* Here is an example how to use the macros and templates: * * // An object to be send around * class Law * { * private: * std::string m_Description; * * public: * * Law(const std::string law) : m_Description(law) * { } * * std::string GetDescription() const * { * return m_Description; * } * }; * * // The NewtonMachine will issue specific events * class NewtonMachine * { * mitkNewMessageMacro(AnalysisStarted); * mitkNewMessage1Macro(AnalysisStopped, bool); * mitkNewMessage1Macro(LawDiscovered, const Law&); * * public: * * void StartAnalysis() * { * // send the "started" signal * m_AnalysisStartedMessage(); * * // we found a new law of nature by creating one :-) * Law massLaw("F=ma"); * m_LawDiscoveredMessage(massLaw); * } * * void StopAnalysis() * { * // send the "stop" message with false, indicating * // that no error occured * m_AnalysisStoppedMessage(false); * } * }; * * class Observer * { * private: * * NewtonMachine* m_Machine; * * public: * * Observer(NewtonMachine* machine) : m_Machine(machine) * { * // Add "observers", i.e. function pointers to the machine * m_Machine->AddAnalysisStartedListener( * ::mitk::MessageDelegate(this, &Observer::MachineStarted)); * m_Machine->AddAnalysisStoppedListener( * ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); * m_Machine->AddLawDiscoveredListener( * ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); * } * * ~Observer() * { * // Always remove your observers when finished * m_Machine->RemoveAnalysisStartedListener( * ::mitk::MessagDelegate(this, &Observer::MachineStarted)); * m_Machine->RemoveAnalysisStoppedListener( * ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); * m_Machine->RemoveLawDiscoveredListener( * ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); * } * * void MachineStarted() * { * std::cout << "Observed machine has started" << std::endl; * } * * void MachineStopped(bool error) * { * std::cout << "Observed machine stopped " << (error ? "with an error" : "") << std::endl; * } * * void LawDiscovered(const Law& law) * { * std::cout << "New law of nature discovered: " << law.GetDescription() << std::endl; * } * }; * * NewtonMachine newtonMachine; * Observer observer(&newtonMachine); * * // This will send two events to registered observers * newtonMachine.StartAnalysis(); * // This will send one event to registered observers * newtonMachine.StopAnalysis(); * * Another example of how to use these message classes can be * found in the directory Testing, file mitkMessageTest.cpp * */ } // namespace #endif diff --git a/Modules/Core/include/mitkPixelType.h b/Modules/Core/include/mitkPixelType.h index 3464c89f37..8df6a5e3b8 100644 --- a/Modules/Core/include/mitkPixelType.h +++ b/Modules/Core/include/mitkPixelType.h @@ -1,299 +1,299 @@ /*============================================================================ 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 mitkPixelType_h #define mitkPixelType_h #include "mitkCommon.h" #include "mitkPixelTypeTraits.h" #include #include #include #include #include #include namespace mitk { template std::string PixelComponentTypeToString() { return itk::ImageIOBase::GetComponentTypeAsString(itk::ImageIOBase::MapPixelType::CType); } template std::string PixelTypeToString() { return std::string(); } /** * @brief Class for defining the data type of pixels * * To obtain additional type information not provided by this class * itk::ImageIOBase can be used by passing the return value of * PixelType::GetItkTypeId() to itk::ImageIOBase::SetPixelTypeInfo * and using the itk::ImageIOBase methods GetComponentType, * GetComponentTypeAsString, GetPixelType, GetPixelTypeAsString. * @ingroup Data */ class MITKCORE_EXPORT PixelType { public: - typedef itk::ImageIOBase::IOPixelType ItkIOPixelType; - typedef itk::ImageIOBase::IOComponentType ItkIOComponentType; + typedef itk::IOPixelEnum ItkIOPixelType; + typedef itk::IOComponentEnum ItkIOComponentType; PixelType(const mitk::PixelType &aPixelType); PixelType &operator=(const PixelType &other); - itk::ImageIOBase::IOPixelType GetPixelType() const; + ItkIOPixelType GetPixelType() const; /** * \brief Get the \a component type (the scalar (!) type). Each element * may contain m_NumberOfComponents (more than one) of these scalars. * */ - int GetComponentType() const; + ItkIOComponentType GetComponentType() const; /** * \brief Returns a string containing the ITK pixel type name. */ std::string GetPixelTypeAsString() const; /** * \brief Returns a string containing the name of the component. */ std::string GetComponentTypeAsString() const; /** * \brief Returns a string representing the pixel type and pixel components. */ std::string GetTypeAsString() const; /** * \brief Get size of the PixelType in bytes * * A RGBA PixelType of floats will return 4 * sizeof(float) */ size_t GetSize() const; /** * \brief Get the number of bits per element (of an * element) * * A vector of double with three components will return * 8*sizeof(double)*3. * \sa GetBitsPerComponent * \sa GetItkTypeId * \sa GetTypeId */ size_t GetBpe() const; /** * \brief Get the number of components of which each element consists * * Each pixel can consist of multiple components, e.g. RGB. */ size_t GetNumberOfComponents() const; /** * \brief Get the number of bits per components * \sa GetBitsPerComponent */ size_t GetBitsPerComponent() const; bool operator==(const PixelType &rhs) const; bool operator!=(const PixelType &rhs) const; ~PixelType(); private: friend PixelType MakePixelType(const itk::ImageIOBase *imageIO); template friend PixelType MakePixelType(std::size_t numOfComponents); template friend PixelType MakePixelType(); template friend PixelType MakePixelType(size_t); - PixelType(const int componentType, - const ItkIOPixelType pixelType, + PixelType(ItkIOComponentType componentType, + ItkIOPixelType pixelType, std::size_t bytesPerComponent, std::size_t numOfComponents, const std::string &componentTypeName, const std::string &pixelTypeName); // default constructor is disabled on purpose PixelType(void); /** \brief the \a type_info of the scalar (!) component type. Each element may contain m_NumberOfComponents (more than one) of these scalars. */ - int m_ComponentType; + ItkIOComponentType m_ComponentType; ItkIOPixelType m_PixelType; std::string m_ComponentTypeName; std::string m_PixelTypeName; std::size_t m_NumberOfComponents; std::size_t m_BytesPerComponent; }; /** * @brief deduct the PixelType for a given vtk image * * @param vtkimagedata the image the PixelType shall be deducted from * @return the mitk::PixelType */ MITKCORE_EXPORT mitk::PixelType MakePixelType(vtkImageData *vtkimagedata); /** * \brief A template method for creating a pixel type. */ template PixelType MakePixelType(std::size_t numOfComponents) { return PixelType(MapPixelType::value>::IOComponentType, MapPixelType::value>::IOPixelType, sizeof(ComponentT), numOfComponents, PixelComponentTypeToString(), PixelTypeToString()); } /** * \brief A template method for creating a pixel type. * * @deprecated, use version with one numOfComponents as function argument instead. */ template PixelType MakePixelType() { return MakePixelType(numOfComponents); } /** * \brief A helper template for compile-time checking of supported ITK image types. * * Unsupported image types will be marked by template specializations with * missing definitions; */ template struct AssertImageTypeIsValid { }; // The itk::VariableLengthVector pixel type is not supported in MITK if it is // used with an itk::Image (it cannot be represented as a mitk::Image object). // Use a itk::VectorImage instead. template struct AssertImageTypeIsValid, VImageDimension>>; /** \brief A template method for creating a MITK pixel type na ITK image type * * \param numOfComponents The number of components for the pixel type of the ITK image */ template PixelType MakePixelType(std::size_t numOfComponents) { AssertImageTypeIsValid(); // define new type, since the ::PixelType is used to distinguish between simple and compound types typedef typename ItkImageType::PixelType ImportPixelType; // get the component type ( is either directly ImportPixelType or ImportPixelType::ValueType for compound types ) typedef typename GetComponentType::ComponentType ComponentT; // The PixelType is the same as the ComponentT for simple types typedef typename ItkImageType::PixelType PixelT; // call the constructor return PixelType(MapPixelType::value>::IOComponentType, MapPixelType::value>::IOPixelType, sizeof(ComponentT), numOfComponents, PixelComponentTypeToString(), PixelTypeToString()); } /** \brief A template method for creating a MITK pixel type from an ITK image * pixel type and dimension * * \param numOfComponents The number of components for the pixel type \c TPixelType */ template PixelType MakePixelType(std::size_t numOfComponents) { typedef typename ImageTypeTrait::ImageType ItkImageType; return MakePixelType(numOfComponents); } /** \brief A template method for creating a MITK pixel type from an ITK image * pixel type and dimension * * For images where the number of components of the pixel type is determined at * runtime (e.g. pixel types like itk::VariableLengthVector) the * MakePixelType(std::size_t) function must be used. */ template PixelType MakePixelType() { if (ImageTypeTrait::IsVectorImage) { mitkThrow() << " Variable pixel type given but the length is not specified. Use the parametric MakePixelType( " "size_t ) method instead."; } // Use the InternalPixelType to get "1" for the number of components in case of // a itk::VectorImage typedef typename ItkImageType::InternalPixelType PixelT; const std::size_t numComp = ComponentsTrait::value, ItkImageType>::Size; // call the constructor return MakePixelType(numComp); } /** * \brief Create a MITK pixel type based on a itk::ImageIOBase object */ inline PixelType MakePixelType(const itk::ImageIOBase *imageIO) { return mitk::PixelType(imageIO->GetComponentType(), imageIO->GetPixelType(), imageIO->GetComponentSize(), imageIO->GetNumberOfComponents(), imageIO->GetComponentTypeAsString(imageIO->GetComponentType()), imageIO->GetPixelTypeAsString(imageIO->GetPixelType())); } /** \brief An interface to the MakePixelType method for creating scalar pixel types. * * Usage: for example MakeScalarPixelType() for a scalar short image */ template PixelType MakeScalarPixelType() { return MakePixelType(); } } // namespace mitk #endif /* mitkPixelType_h */ diff --git a/Modules/Core/include/mitkPixelTypeMultiplex.h b/Modules/Core/include/mitkPixelTypeMultiplex.h index ed235bd36b..94efdb34de 100644 --- a/Modules/Core/include/mitkPixelTypeMultiplex.h +++ b/Modules/Core/include/mitkPixelTypeMultiplex.h @@ -1,173 +1,173 @@ /*============================================================================ 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 MITKPIXELTYPEMULTIPLEX_H #define MITKPIXELTYPEMULTIPLEX_H #define mitkPixelTypeMultiplex0(function, ptype) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype); \ } #define mitkPixelTypeMultiplex1(function, ptype, param1) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1); \ } #define mitkPixelTypeMultiplex2(function, ptype, param1, param2) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2); \ } #define mitkPixelTypeMultiplex3(function, ptype, param1, param2, param3) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2, param3); \ } // we have to have a default for, else Clang 3.6.1 complains about problems if 'if evaluates to false' // therefore if type does not match double is assumed #define mitkPixelTypeMultiplex4(function, ptype, param1, param2, param3, param4) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2, param3, param4); \ else if (true) \ function(ptype, param1, param2, param3, param4); \ } // we have to have a default for, else Clang 3.6.1 complains about problems if 'if evaluates to false' // therefore if type does not match double is assumed #define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2, param3, param4, param5); \ else \ function(ptype, param1, param2, param3, param4, param5); \ } #endif // MITKPIXELTYPEMULTIPLEX_H diff --git a/Modules/Core/include/mitkPixelTypeTraits.h b/Modules/Core/include/mitkPixelTypeTraits.h index 16772df28d..a5f190d33a 100644 --- a/Modules/Core/include/mitkPixelTypeTraits.h +++ b/Modules/Core/include/mitkPixelTypeTraits.h @@ -1,271 +1,272 @@ /*============================================================================ 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 PIXELTYPETRAITS_H #define PIXELTYPETRAITS_H #include #include #include #include #include #include /** \file mitkPixelTypeTraits.h * * The pixel type traits are in general used for compile time resolution of the component type and * the number of components for compound types like the ones in ItkImageType. * The default values are used to define the corresponding variable also for scalar types */ namespace itk { /** Forward declaration of the Variable Length Vector class from ITK */ template class VariableLengthVector; } #define MITK_PIXEL_COMPONENT_TYPE(type, ctype, name) \ template <> \ struct mitk::MapPixelComponentType \ { \ static const int value = ctype; \ } template <> \ std::string mitk::PixelComponentTypeToString() \ { \ return name; \ } namespace mitk { - static const int PixelUserType = itk::ImageIOBase::MATRIX + 1; - static const int PixelComponentUserType = itk::ImageIOBase::DOUBLE + 1; + static const int PixelUserType = static_cast(itk::IOPixelEnum::MATRIX) + 1; + static const int PixelComponentUserType = static_cast(itk::IOComponentEnum::DOUBLE) + 1; /** * Maps pixel component types (primitive types like int, short, double, etc. and custom * types) to and integer constant. Specialize this template for custom types by using the * #MITK_PIXEL_COMPONENT_TYPE macro. */ template struct MapPixelComponentType { - static const int value = itk::ImageIOBase::MapPixelType::CType; + static const itk::IOComponentEnum value = itk::ImageIOBase::MapPixelType::CType; }; /** \brief This is an implementation of a type trait to provide a compile-time check for PixelType used in the instantiation of an itk::Image */ template struct isPrimitiveType { static const bool value = false; }; /** \brief Provides a partial specialization for the \sa isPrimitiveType object */ #define DEFINE_TYPE_PRIMITIVE(_TYPEIN) \ template <> \ struct isPrimitiveType<_TYPEIN> \ { \ static const bool value = true; \ } /** \brief Partial specialization (unsigned char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned char); /** \brief Partial specialization (char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(char); /** \brief Partial specialization (signed char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(signed char); /** \brief Partial specialization (unsigned short) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned short); /** \brief Partial specialization (short) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(short); /** \brief Partial specialization (unsigned int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned int); /** \brief Partial specialization (int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(int); /** \brief Partial specialization (long int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(long int); /** \brief Partial specialization (long unsigned int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(long unsigned int); /** \brief Partial specialization (float) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(float); /** \brief Partial specialization (double) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(double); template struct ImageTypeTrait { typedef itk::Image ImageType; static const bool IsVectorImage = false; }; template struct ImageTypeTrait, VDimension> { typedef itk::VectorImage ImageType; static const bool IsVectorImage = true; }; template struct ImageTypeTrait { typedef T ImageType; static const bool IsVectorImage = false; }; template struct ImageTypeTrait, 0> { typedef itk::VectorImage ImageType; static const bool IsVectorImage = true; }; /** \brief Compile-time trait for resolving the ValueType from an ItkImageType */ template struct PixelTypeTrait { typedef T ValueType; }; /** \brief Partial specialization for the PixelTypeTrait * * Specialization for the false value. Used to define the value type for non-primitive pixel types */ template struct PixelTypeTrait { typedef typename T::ValueType ValueType; }; /** \brief Compile time resolving of the type of a component */ template struct GetComponentType { typedef typename PixelTypeTrait::value, T>::ValueType ComponentType; }; /** \brief Object for compile-time resolving of the number of components for given type. * * Default value for the component number is 1 */ template struct ComponentsTrait { static const size_t Size = 1; }; /** \brief Partial specialization for the ComponentsTraits in case of compound types */ template struct ComponentsTrait { static const size_t Size = T::ValueType::Length; }; - typedef itk::ImageIOBase::IOPixelType itkIOPixelType; + typedef itk::IOPixelEnum itkIOPixelType; + typedef itk::IOComponentEnum itkIOComponentType; /** \brief Object for compile-time translation of a composite pixel type into an itk::ImageIOBase::IOPixelType * information * * The default value of the IOCompositeType is the UNKNOWNPIXELTYPE, the default value will be used for all but the * types below with own partial specialization. The values of the IOCompositeType member in the specializations * correspond * to the values of the itk::ImageIOBase::IOPixelType enum values. */ template struct MapCompositePixelType { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::UNKNOWNPIXELTYPE; + static const itkIOPixelType IOCompositeType = itkIOPixelType::UNKNOWNPIXELTYPE; }; //------------------------ // Partial template specialization for fixed-length types //------------------------ template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::RGB; + static const itkIOPixelType IOCompositeType = itkIOPixelType::RGB; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::RGBA; + static const itkIOPixelType IOCompositeType = itkIOPixelType::RGBA; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::DIFFUSIONTENSOR3D; + static const itkIOPixelType IOCompositeType = itkIOPixelType::DIFFUSIONTENSOR3D; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::VECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR; }; //------------------------ // Partial template specialization for variable-length types //------------------------ template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::VECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::COVARIANTVECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::COVARIANTVECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::MATRIX; + static const itkIOPixelType IOCompositeType = itkIOPixelType::MATRIX; }; /** \brief Object for compile-time translation of a pixel type into an itk::ImageIOBase::IOPixelType information * * The first template parameter is the pixel type to be translated, the second parameter determines the processing * way. For non-primitive types the first template parameter is passed to the MapCompositePixelType object to be * resolved there * for primitive types the value is set to SCALAR. * * To initalize the flag correctly in compile-time use the \sa isPrimitiveType trait. */ template struct MapPixelType { static const itkIOPixelType IOPixelType = MapCompositePixelType::IOCompositeType; - static const int IOComponentType = MapPixelComponentType::ComponentType>::value; + static const itkIOComponentType IOComponentType = MapPixelComponentType::ComponentType>::value; }; /** \brief Partial specialization for setting the IOPixelType for primitive types to SCALAR */ template struct MapPixelType { - static const itkIOPixelType IOPixelType = itk::ImageIOBase::SCALAR; - static const int IOComponentType = MapPixelComponentType::value; + static const itkIOPixelType IOPixelType = itkIOPixelType::SCALAR; + static const itkIOComponentType IOComponentType = MapPixelComponentType::value; }; } // end namespace mitk #endif // PIXELTYPETRAITS_H diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index d818815efd..c5832a5888 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -1,254 +1,254 @@ /*============================================================================ 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 PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D #define PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D #include "mitkBaseProperty.h" #include "mitkGenericProperty.h" #include "mitkUIDGenerator.h" #include "mitkIPropertyOwner.h" #include #include #include #include namespace mitk { class XMLWriter; /** * @brief Key-value list holding instances of BaseProperty * * This list is meant to hold an arbitrary list of "properties", * which should describe the object associated with this list. * * Usually you will use PropertyList as part of a DataNode * object - in this context the properties describe the data object * held by the DataNode (e.g. whether the object is rendered at * all, which color is used for rendering, what name should be * displayed for the object, etc.) * * The values in the list are not fixed, you may introduce any kind * of property that seems useful - all you have to do is inherit * from BaseProperty. * * The list is organized as a key-value pairs, i.e. * * \li "name" : pointer to a StringProperty * \li "visible" : pointer to a BoolProperty * \li "color" : pointer to a ColorProperty * \li "volume" : pointer to a FloatProperty * * Please see the documentation of SetProperty and ReplaceProperty for two * quite different semantics. Normally SetProperty is what you want - this * method will try to change the value of an existing property and will * not allow you to replace e.g. a ColorProperty with an IntProperty. * * Please also regard, that the key of a property must be a none empty string. * This is a precondition. Setting properties with empty keys will raise an exception. * * @ingroup DataManagement */ class MITKCORE_EXPORT PropertyList : public itk::Object, public IPropertyOwner { public: mitkClassMacroItkParent(PropertyList, itk::Object); /** * Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Map structure to hold the properties: the map key is a string, * the value consists of the actual property object (BaseProperty). */ typedef std::map PropertyMap; typedef std::pair PropertyMapElementType; // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * @brief Get a property by its name. */ mitk::BaseProperty *GetProperty(const std::string &propertyKey) const; /** * @brief Set a property object in the list/map by reference. * * The actual OBJECT holding the value of the property is replaced by this function. * This is useful if you want to change the type of the property, like from BoolProperty to StringProperty. * Another use is to share one and the same property object among several ProperyList/DataNode objects, which * makes them appear synchronized. */ void ReplaceProperty(const std::string &propertyKey, BaseProperty *property); /** * @brief Set a property object in the list/map by reference. */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); //##Documentation //## @brief Convenience access method for GenericProperty properties //## (T being the type of the second parameter) //## @return @a true property was found template bool GetPropertyValue(const char *propertyKey, T &value) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /** * @brief Convenience method to access the value of a BoolProperty */ bool GetBoolProperty(const char *propertyKey, bool &boolValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, bool &boolValue) const; /** * @brief Convenience method to set the value of a BoolProperty */ void SetBoolProperty(const char *propertyKey, bool boolValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, bool boolValue); /** * @brief Convenience method to access the value of an IntProperty */ bool GetIntProperty(const char *propertyKey, int &intValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, int &intValue) const; /** * @brief Convenience method to set the value of an IntProperty */ void SetIntProperty(const char *propertyKey, int intValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, int intValue); /** * @brief Convenience method to access the value of a FloatProperty */ bool GetFloatProperty(const char *propertyKey, float &floatValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, float &floatValue) const; /** * @brief Convenience method to set the value of a FloatProperty */ void SetFloatProperty(const char *propertyKey, float floatValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, float floatValue); /** * @brief Convenience method to access the value of a DoubleProperty */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, double &doubleValue) const; /** * @brief Convenience method to set the value of a DoubleProperty */ void SetDoubleProperty(const char *propertyKey, double doubleValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, double doubleValue); /** * @brief Convenience method to access the value of a StringProperty */ bool GetStringProperty(const char *propertyKey, std::string &stringValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, std::string &stringValue) const; /** * @brief Convenience method to set the value of a StringProperty */ void SetStringProperty(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const std::string &stringValue); /** * @brief Get the timestamp of the last change of the map or the last change of one of * the properties store in the list (whichever is later). */ - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; /** * @brief Remove a property from the list/map. */ bool DeleteProperty(const std::string &propertyKey); const PropertyMap *GetMap() const { return &m_Properties; } bool IsEmpty() const { return m_Properties.empty(); } virtual void Clear(); protected: PropertyList(); PropertyList(const PropertyList &other); ~PropertyList() override; /** * @brief Map of properties. */ PropertyMap m_Properties; private: itk::LightObject::Pointer InternalClone() const override; }; } // namespace mitk #endif /* PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D */ diff --git a/Modules/Core/include/mitkStandaloneDataStorage.h b/Modules/Core/include/mitkStandaloneDataStorage.h index 88e17af581..f026104ac9 100644 --- a/Modules/Core/include/mitkStandaloneDataStorage.h +++ b/Modules/Core/include/mitkStandaloneDataStorage.h @@ -1,129 +1,129 @@ /*============================================================================ 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 MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ #define MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ #include "itkVectorContainer.h" #include "mitkDataStorage.h" #include "mitkMessage.h" #include +#include namespace mitk { class NodePredicateBase; class DataNode; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The StandaloneDataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects that were used to create the new object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## It is derived from mitk::DataStorage and implements its interface, //## including AddNodeEvent and RemoveNodeEvent. //## @ingroup StandaloneDataStorage class MITKCORE_EXPORT StandaloneDataStorage : public mitk::DataStorage { public: mitkClassMacro(StandaloneDataStorage, mitk::DataStorage); itkFactorylessNewMacro(Self); itkCloneMacro(Self); //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the StandaloneDataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. void Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents = nullptr) override; //##Documentation //## @brief Removes node from the StandaloneDataStorage //## void Remove(const mitk::DataNode *node) override; //##Documentation //## @brief Checks if a node exists in the StandaloneDataStorage //## bool Exists(const mitk::DataNode *node) const override; //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## SetOfObjects::ConstPointer GetSources(const mitk::DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const override; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. SetOfObjects::ConstPointer GetDerivations(const mitk::DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const override; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## SetOfObjects::ConstPointer GetAll() const override; - /*ITK Mutex */ - mutable itk::SimpleFastMutexLock m_Mutex; + mutable std::mutex m_Mutex; protected: //##Documentation //## @brief noncyclical directed graph data structure to store the nodes with their relation typedef std::map AdjacencyList; StandaloneDataStorage(); ~StandaloneDataStorage() override; //##Documentation //## @brief convenience method to check if the object has been initialized (i.e. a data tree has been set) bool IsInitialized() const; //##Documentation //## @brief Traverses the Relation graph and extracts a list of related elements (e.g. Sources or Derivations) SetOfObjects::ConstPointer GetRelations(const mitk::DataNode *node, const AdjacencyList &relation, const NodePredicateBase *condition = nullptr, bool onlyDirectlyRelated = true) const; //##Documentation //## @brief deletes all references to a node in a given relation (used in Remove() and TreeListener) void RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation); //##Documentation //## @brief Prints the contents of the StandaloneDataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; //##Documentation //## @brief Nodes and their relation are stored in m_SourceNodes AdjacencyList m_SourceNodes; //##Documentation //## @brief Nodes are stored in reverse relation for easier traversal in the opposite direction of the relation AdjacencyList m_DerivedNodes; }; } // namespace mitk #endif /* MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ */ diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp index e448408faa..d6ba0822f6 100644 --- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp +++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp @@ -1,281 +1,281 @@ /*============================================================================ 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 #include #include struct mitk::ExtractSliceFilter2::Impl { Impl(); ~Impl(); PlaneGeometry::Pointer OutputGeometry; mitk::ExtractSliceFilter2::Interpolator Interpolator; itk::Object::Pointer InterpolateImageFunction; }; mitk::ExtractSliceFilter2::Impl::Impl() : Interpolator(NearestNeighbor) { } mitk::ExtractSliceFilter2::Impl::~Impl() { } namespace { template void CreateInterpolateImageFunction(const TInputImage* inputImage, mitk::ExtractSliceFilter2::Interpolator interpolator, itk::Object::Pointer& result) { typename itk::InterpolateImageFunction::Pointer interpolateImageFunction; switch (interpolator) { case mitk::ExtractSliceFilter2::NearestNeighbor: interpolateImageFunction = itk::NearestNeighborInterpolateImageFunction::New().GetPointer(); break; case mitk::ExtractSliceFilter2::Linear: interpolateImageFunction = itk::LinearInterpolateImageFunction::New().GetPointer(); break; case mitk::ExtractSliceFilter2::Cubic: { auto bSplineInterpolateImageFunction = itk::BSplineInterpolateImageFunction::New(); bSplineInterpolateImageFunction->SetSplineOrder(2); interpolateImageFunction = bSplineInterpolateImageFunction.GetPointer(); break; } default: mitkThrow() << "Interplator is unknown."; } interpolateImageFunction->SetInputImage(inputImage); result = interpolateImageFunction.GetPointer(); } template void GenerateData(const itk::Image* inputImage, mitk::Image* outputImage, const mitk::ExtractSliceFilter2::OutputImageRegionType& outputRegion, itk::Object* interpolateImageFunction) { typedef itk::Image TInputImage; typedef itk::InterpolateImageFunction TInterpolateImageFunction; auto outputGeometry = outputImage->GetSlicedGeometry()->GetPlaneGeometry(0); auto interpolator = static_cast(interpolateImageFunction); auto origin = outputGeometry->GetOrigin(); auto spacing = outputGeometry->GetSpacing(); auto xDirection = outputGeometry->GetAxisVector(0); auto yDirection = outputGeometry->GetAxisVector(1); xDirection.Normalize(); yDirection.Normalize(); auto spacingAlongXDirection = xDirection * spacing[0]; auto spacingAlongYDirection = yDirection * spacing[1]; const std::size_t pixelSize = outputImage->GetPixelType().GetSize(); const std::size_t width = outputGeometry->GetExtent(0); const std::size_t xBegin = outputRegion.GetIndex(0); const std::size_t yBegin = outputRegion.GetIndex(1); const std::size_t xEnd = xBegin + outputRegion.GetSize(0); const std::size_t yEnd = yBegin + outputRegion.GetSize(1); mitk::ImageWriteAccessor writeAccess(outputImage, nullptr, mitk::ImageAccessorBase::IgnoreLock); auto data = static_cast(writeAccess.GetData()); const TPixel backgroundPixel = std::numeric_limits::lowest(); TPixel pixel; itk::ContinuousIndex index; mitk::Point3D yPoint; mitk::Point3D point; for (std::size_t y = yBegin; y < yEnd; ++y) { yPoint = origin + spacingAlongYDirection * y; for (std::size_t x = xBegin; x < xEnd; ++x) { point = yPoint + spacingAlongXDirection * x; if (inputImage->TransformPhysicalPointToContinuousIndex(point, index)) { pixel = interpolator->EvaluateAtContinuousIndex(index); memcpy(static_cast(data + pixelSize * (width * y + x)), static_cast(&pixel), pixelSize); } else { memcpy(static_cast(data + pixelSize * (width * y + x)), static_cast(&backgroundPixel), pixelSize); } } } } void VerifyInputImage(const mitk::Image* inputImage) { auto dimension = inputImage->GetDimension(); if (3 != dimension) mitkThrow() << "Input images with " << dimension << " dimensions are not supported."; if (!inputImage->IsInitialized()) mitkThrow() << "Input image is not initialized."; if (!inputImage->IsVolumeSet()) mitkThrow() << "Input image volume is not set."; auto geometry = inputImage->GetGeometry(); if (nullptr == geometry || !geometry->IsValid()) mitkThrow() << "Input image has invalid geometry."; if (!geometry->GetImageGeometry()) mitkThrow() << "Geometry of input image is not an image geometry."; } void VerifyOutputGeometry(const mitk::PlaneGeometry* outputGeometry) { if (nullptr == outputGeometry) mitkThrow() << "Output geometry is not set."; if (!outputGeometry->GetImageGeometry()) mitkThrow() << "Output geometry is not an image geometry."; } } mitk::ExtractSliceFilter2::ExtractSliceFilter2() : m_Impl(new Impl) { } mitk::ExtractSliceFilter2::~ExtractSliceFilter2() { delete m_Impl; } void mitk::ExtractSliceFilter2::AllocateOutputs() { const auto* inputImage = this->GetInput(); const auto* outputGeometry = this->GetOutputGeometry(); auto outputImage = this->GetOutput(); auto pixelType = inputImage->GetPixelType(); outputImage->Initialize(pixelType, 1, *outputGeometry); auto data = new char[static_cast(pixelType.GetSize() * outputGeometry->GetExtent(0) * outputGeometry->GetExtent(1))]; try { if (!outputImage->SetImportVolume(data, 0, 0, mitk::Image::ReferenceMemory)) throw; } catch (...) { delete[] data; } } /*void mitk::ExtractSliceFilter2::BeforeThreadedGenerateData() { if (nullptr != m_Impl->InterpolateImageFunction && this->GetInput()->GetMTime() < this->GetMTime()) return; const auto* inputImage = this->GetInput(); AccessFixedDimensionByItk_2(inputImage, CreateInterpolateImageFunction, 3, this->GetInterpolator(), m_Impl->InterpolateImageFunction); } void mitk::ExtractSliceFilter2::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType) { const auto* inputImage = this->GetInput(); AccessFixedDimensionByItk_3(inputImage, ::GenerateData, 3, this->GetOutput(), outputRegionForThread, m_Impl->InterpolateImageFunction); }*/ void mitk::ExtractSliceFilter2::GenerateData() { if (nullptr != m_Impl->InterpolateImageFunction && this->GetInput()->GetMTime() < this->GetMTime()) return; const auto* inputImage = this->GetInput(); AccessFixedDimensionByItk_2(inputImage, CreateInterpolateImageFunction, 3, this->GetInterpolator(), m_Impl->InterpolateImageFunction); this->AllocateOutputs(); auto outputRegion = this->GetOutput()->GetLargestPossibleRegion(); AccessFixedDimensionByItk_3(inputImage, ::GenerateData, 3, this->GetOutput(), outputRegion, m_Impl->InterpolateImageFunction); } void mitk::ExtractSliceFilter2::SetInput(const InputImageType* image) { if (this->GetInput() == image) return; Superclass::SetInput(image); m_Impl->InterpolateImageFunction = nullptr; } void mitk::ExtractSliceFilter2::SetInput(unsigned int index, const InputImageType* image) { if (0 != index) mitkThrow() << "Input index " << index << " is invalid."; this->SetInput(image); } const mitk::PlaneGeometry* mitk::ExtractSliceFilter2::GetOutputGeometry() const { return m_Impl->OutputGeometry; } void mitk::ExtractSliceFilter2::SetOutputGeometry(PlaneGeometry::Pointer outputGeometry) { if (m_Impl->OutputGeometry != outputGeometry) { m_Impl->OutputGeometry = outputGeometry; this->Modified(); } } mitk::ExtractSliceFilter2::Interpolator mitk::ExtractSliceFilter2::GetInterpolator() const { return m_Impl->Interpolator; } void mitk::ExtractSliceFilter2::SetInterpolator(Interpolator interpolator) { if (m_Impl->Interpolator != interpolator) { m_Impl->Interpolator = interpolator; m_Impl->InterpolateImageFunction = nullptr; this->Modified(); } } -void mitk::ExtractSliceFilter2::VerifyInputInformation() +void mitk::ExtractSliceFilter2::VerifyInputInformation() const { Superclass::VerifyInputInformation(); VerifyInputImage(this->GetInput()); VerifyOutputGeometry(this->GetOutputGeometry()); } diff --git a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp index 3907e3b40b..6802fb3c62 100644 --- a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp +++ b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp @@ -1,125 +1,122 @@ /*============================================================================ 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. ============================================================================*/ #if (_MSC_VER == 1200) #include #endif #include "mitkHistogramGenerator.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" // // The new ITK Statistics framework has // a class with the same functionality as // MITKScalarImageToHistogramGenerator.h, but // no longer has the classis the MITK class depends on. #if !defined(ITK_USE_REVIEW_STATISTICS) #include "itkMITKScalarImageToHistogramGenerator.h" #else #include "itkScalarImageToHistogramGenerator.h" #endif mitk::HistogramGenerator::HistogramGenerator() : m_Image(nullptr), m_Size(256), m_Histogram(nullptr) { } mitk::HistogramGenerator::~HistogramGenerator() { } template void InternalCompute(itk::Image *itkImage, const mitk::HistogramGenerator *mitkHistoGenerator, mitk::HistogramGenerator::HistogramType::ConstPointer &histogram) { #if !defined(ITK_USE_REVIEW_STATISTICS) typedef itk::Statistics::MITKScalarImageToHistogramGenerator, double> HistogramGeneratorType; #else typedef itk::Statistics::ScalarImageToHistogramGenerator> HistogramGeneratorType; #endif typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New(); histogramGenerator->SetInput(itkImage); histogramGenerator->SetNumberOfBins(mitkHistoGenerator->GetSize()); // histogramGenerator->SetMarginalScale( 10.0 ); histogramGenerator->Compute(); histogram = histogramGenerator->GetOutput(); } void mitk::HistogramGenerator::ComputeHistogram() { if ((m_Histogram.IsNull()) || (m_Histogram->GetMTime() < m_Image->GetMTime())) { const_cast(m_Image.GetPointer())->SetRequestedRegionToLargestPossibleRegion(); //@todo without this, // Image::GetScalarMin // does not work for // dim==3 (including // sliceselector!) const_cast(m_Image.GetPointer())->Update(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(0); timeSelector->UpdateLargestPossibleRegion(); AccessByItk_n(timeSelector->GetOutput(), InternalCompute, (this, m_Histogram)); } // debug code /* MITK_INFO << "Histogram modfied 1" << m_Histogram->GetMTime() << std::endl; m_Histogram->Modified(); MITK_INFO << "Histogram modfied 2" << m_Histogram->GetMTime() << std::endl; MITK_INFO << "Image modfied" << m_Image->GetMTime() << std::endl; const unsigned int histogramSize = m_Histogram->Size(); MITK_INFO << "Histogram size " << histogramSize << std::endl; HistogramType::ConstIterator itr = GetHistogram()->Begin(); HistogramType::ConstIterator end = GetHistogram()->End(); int bin = 0; while( itr != end ) { MITK_INFO << "bin = " << GetHistogram()->GetBinMin(0,bin) << "--" << GetHistogram()->GetBinMax(0,bin) << " frequency = "; MITK_INFO << itr.GetFrequency() << std::endl; ++itr; ++bin; } */ } float mitk::HistogramGenerator::GetMaximumFrequency() const { return CalculateMaximumFrequency(this->m_Histogram); -}; +} float mitk::HistogramGenerator::CalculateMaximumFrequency(const HistogramType *histogram) { HistogramType::ConstIterator itr = histogram->Begin(); HistogramType::ConstIterator end = histogram->End(); float maxFreq = 0; while (itr != end) { - maxFreq = vnl_math_max(maxFreq, - // get rid of ambiguity with type signature - // for vnl_math_max - static_cast(itr.GetFrequency())); + maxFreq = std::max(maxFreq, static_cast(itr.GetFrequency())); ++itr; } return maxFreq; -}; +} diff --git a/Modules/Core/src/Algorithms/mitkImageSource.cpp b/Modules/Core/src/Algorithms/mitkImageSource.cpp index 8ebc72332d..7a4dc03182 100644 --- a/Modules/Core/src/Algorithms/mitkImageSource.cpp +++ b/Modules/Core/src/Algorithms/mitkImageSource.cpp @@ -1,199 +1,201 @@ /*============================================================================ 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 "mitkImageSource.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" +#include + mitk::ImageSource::ImageSource() { // Create the output. We use static_cast<> here because we know the default // output must be of type TOutputImage OutputImageType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output.GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(DataObjectPointerArraySizeType /*idx*/) { return static_cast(mitk::Image::New().GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(const DataObjectIdentifierType &name) { itkDebugMacro("MakeOutput(" << name << ")"); if (this->IsIndexedOutputName(name)) { return this->MakeOutput(this->MakeIndexFromOutputName(name)); } return static_cast(mitk::Image::New().GetPointer()); } //---------------------------------------------------------------------------- unsigned int mitk::ImageSource::SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion) { // Get the output pointer OutputImageType *outputPtr = this->GetOutput(); const SlicedData::SizeType &requestedRegionSize = outputPtr->GetRequestedRegion().GetSize(); int splitAxis; SlicedData::IndexType splitIndex; SlicedData::SizeType splitSize; // Initialize the splitRegion to the output requested region splitRegion = outputPtr->GetRequestedRegion(); splitIndex = splitRegion.GetIndex(); splitSize = splitRegion.GetSize(); // split on the outermost dimension available splitAxis = outputPtr->GetDimension() - 1; while (requestedRegionSize[splitAxis] == 1) { --splitAxis; if (splitAxis < 0) { // cannot split itkDebugMacro(" Cannot Split"); return 1; } } // determine the actual number of pieces that will be generated SlicedData::SizeType::SizeValueType range = requestedRegionSize[splitAxis]; auto valuesPerThread = itk::Math::Ceil(range / (double)num); unsigned int maxThreadIdUsed = itk::Math::Ceil(range / (double)valuesPerThread) - 1; // Split the region if (i < maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; splitSize[splitAxis] = valuesPerThread; } if (i == maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; // last thread needs to process the "rest" dimension being split splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread; } // set the split region ivars splitRegion.SetIndex(splitIndex); splitRegion.SetSize(splitSize); itkDebugMacro(" Split Piece: " << splitRegion); return maxThreadIdUsed + 1; } //---------------------------------------------------------------------------- void mitk::ImageSource::AllocateOutputs() { OutputImagePointer outputPtr; // Allocate the output memory for (unsigned int i = 0; i < this->GetNumberOfOutputs(); i++) { outputPtr = this->GetOutput(i); // outputPtr->SetBufferedRegion(outputPtr->GetRequestedRegion()); @FIXME??? // outputPtr->Allocate(); @FIXME??? } } //---------------------------------------------------------------------------- void mitk::ImageSource::GenerateData() { // Call a method that can be overriden by a subclass to allocate // memory for the filter's outputs this->AllocateOutputs(); // Call a method that can be overridden by a subclass to perform // some calculations prior to splitting the main computations into // separate threads this->BeforeThreadedGenerateData(); // Set up the multithreaded processing ThreadStruct str; str.Filter = this; - this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads()); + this->GetMultiThreader()->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str); // multithread the execution this->GetMultiThreader()->SingleMethodExecute(); // Call a method that can be overridden by a subclass to perform // some calculations after all the threads have completed this->AfterThreadedGenerateData(); } //---------------------------------------------------------------------------- // The execute method created by the subclass. void mitk::ImageSource::ThreadedGenerateData(const OutputImageRegionType &, itk::ThreadIdType) { itkExceptionMacro("subclass should override this method!!!"); } // Callback routine used by the threading library. This routine just calls // the ThreadedGenerateData method after setting the correct region for this // thread. -ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg) +itk::ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg) { ThreadStruct *str; itk::ThreadIdType total, threadId, threadCount; - threadId = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->ThreadID; - threadCount = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads; + threadId = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->WorkUnitID; + threadCount = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->NumberOfWorkUnits; - str = (ThreadStruct *)(((itk::MultiThreader::ThreadInfoStruct *)(arg))->UserData); + str = (ThreadStruct *)(((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->UserData); // execute the actual method with appropriate output region // first find out how many pieces extent can be split into. SlicedData::RegionType splitRegion; total = str->Filter->SplitRequestedRegion(threadId, threadCount, splitRegion); if (threadId < total) { str->Filter->ThreadedGenerateData(splitRegion, threadId); } // else // { // otherwise don't use this thread. Sometimes the threads dont // break up very well and it is just as efficient to leave a // few threads idle. // } - return ITK_THREAD_RETURN_VALUE; + return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; } void mitk::ImageSource::PrepareOutputs() { Superclass::PrepareOutputs(); } vtkImageData *mitk::ImageSource::GetVtkImageData() { Update(); return GetOutput()->GetVtkImageData(); } const vtkImageData *mitk::ImageSource::GetVtkImageData() const { return GetOutput()->GetVtkImageData(); } mitkBaseDataSourceGetOutputDefinitions(mitk::ImageSource) diff --git a/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp b/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp index 5de2fdba82..130adf3ce4 100644 --- a/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp @@ -1,161 +1,161 @@ /*============================================================================ 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. ============================================================================*/ // mitk includes #include "mitkMultiComponentImageDataComparisonFilter.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageReadAccessor.h" // other includes // #include namespace mitk { MultiComponentImageDataComparisonFilter::MultiComponentImageDataComparisonFilter() : ImageToImageFilter(), m_Tolerance(0.0f), m_CompareResult(false), m_CompareDetails(nullptr) { this->SetNumberOfRequiredInputs(2); } MultiComponentImageDataComparisonFilter::~MultiComponentImageDataComparisonFilter() {} bool MultiComponentImageDataComparisonFilter::GetResult(double threshold) { if (!m_CompareResult) { return false; } if (m_CompareDetails->m_PixelsWithDifference > threshold) { return false; } return true; } void MultiComponentImageDataComparisonFilter::SetValidImage(const Image *_arg) { this->SetInput(0, _arg); } void MultiComponentImageDataComparisonFilter::SetTestImage(const Image *_arg) { this->SetInput(1, _arg); } const Image *MultiComponentImageDataComparisonFilter::GetValidImage() { return this->GetInput(0); } const Image *MultiComponentImageDataComparisonFilter::GetTestImage() { return this->GetInput(1); } void MultiComponentImageDataComparisonFilter::SetCompareFilterResult(CompareFilterResults *results) { m_CompareDetails = results; } CompareFilterResults *MultiComponentImageDataComparisonFilter::GetCompareFilterResult() { return m_CompareDetails; } void MultiComponentImageDataComparisonFilter::GenerateData() { // check inputs const Image *testInput = this->GetTestImage(); const Image *validInput = this->GetValidImage(); // Generally this filter is part of the mitk::Image::Equal() method and only checks the equality of the image data // so no further image type comparison is performed! // CAVE: If the images differ in a parameter other then the image data, the filter may fail!! PixelType type = validInput->GetPixelType(); - if (type.GetComponentType() == itk::ImageIOBase::CHAR) + if (type.GetComponentType() == itk::IOComponentEnum::CHAR) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::UCHAR) + else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::INT) + else if (type.GetComponentType() == itk::IOComponentEnum::INT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::UINT) + else if (type.GetComponentType() == itk::IOComponentEnum::UINT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::SHORT) + else if (type.GetComponentType() == itk::IOComponentEnum::SHORT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::USHORT) + else if (type.GetComponentType() == itk::IOComponentEnum::USHORT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::LONG) + else if (type.GetComponentType() == itk::IOComponentEnum::LONG) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::ULONG) + else if (type.GetComponentType() == itk::IOComponentEnum::ULONG) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::FLOAT) + else if (type.GetComponentType() == itk::IOComponentEnum::FLOAT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::DOUBLE) + else if (type.GetComponentType() == itk::IOComponentEnum::DOUBLE) { CompareMultiComponentImage(testInput, validInput); } else { mitkThrow() << "Pixel component type not supported!"; } } template void mitk::MultiComponentImageDataComparisonFilter::CompareMultiComponentImage(const Image *testImage, const Image *validImage) { unsigned int noOfTimes = validImage->GetDimension(3); unsigned int noOfPixels = validImage->GetDimension(0) * validImage->GetDimension(1) * validImage->GetDimension(2); unsigned int noOfComponents = validImage->GetPixelType().GetNumberOfComponents(); for (unsigned int t = 0; t < noOfTimes; ++t) { ImageReadAccessor readAccTImage(testImage, testImage->GetVolumeData(t)); ImageReadAccessor readAccVImage(validImage, validImage->GetVolumeData(t)); for (unsigned int p = 0; p < noOfPixels * noOfComponents; ++p) { TPixel vDataItem = static_cast(const_cast(readAccVImage.GetData()))[p]; TPixel tDataItem = static_cast(const_cast(readAccTImage.GetData()))[p]; if (std::abs(static_cast(tDataItem - vDataItem)) > m_Tolerance) { ++m_CompareDetails->m_PixelsWithDifference; m_CompareDetails->m_MaximumDifference = std::max(m_CompareDetails->m_MaximumDifference, std::abs(static_cast(tDataItem - vDataItem))); double min = std::min(m_CompareDetails->m_MinimumDifference, std::abs(static_cast(tDataItem - vDataItem))); if (min != 0.0f) // a difference of zero is not a difference! m_CompareDetails->m_MinimumDifference = min; m_CompareDetails->m_TotalDifference += std::abs(static_cast(tDataItem - vDataItem)); } } } if (m_CompareDetails->m_PixelsWithDifference > 0) { m_CompareDetails->m_MeanDifference = m_CompareDetails->m_TotalDifference / m_CompareDetails->m_PixelsWithDifference; m_CompareResult = false; } else { m_CompareResult = true; } m_CompareDetails->m_FilterCompleted = true; } } // end namespace mitk diff --git a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp index 2ebeffa059..86fe47f153 100644 --- a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp @@ -1,211 +1,211 @@ /*============================================================================ 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 "mitkRGBToRGBACastImageFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkImageToItk.h" #include "mitkProperties.h" #include #include #include #include mitk::RGBToRGBACastImageFilter::RGBToRGBACastImageFilter() { this->SetNumberOfIndexedInputs(1); this->SetNumberOfRequiredInputs(1); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); } mitk::RGBToRGBACastImageFilter::~RGBToRGBACastImageFilter() { } bool mitk::RGBToRGBACastImageFilter::IsRGBImage(const mitk::Image *image) { const mitk::PixelType &inputPixelType = image->GetPixelType(); - if ((inputPixelType.GetPixelType() == itk::ImageIOBase::RGB) && - ((inputPixelType.GetComponentType() == itk::ImageIOBase::UCHAR) || - (inputPixelType.GetComponentType() == itk::ImageIOBase::USHORT) || - (inputPixelType.GetComponentType() == itk::ImageIOBase::FLOAT) || - (inputPixelType.GetComponentType() == itk::ImageIOBase::DOUBLE))) + if ((inputPixelType.GetPixelType() == itk::IOPixelEnum::RGB) && + ((inputPixelType.GetComponentType() == itk::IOComponentEnum::UCHAR) || + (inputPixelType.GetComponentType() == itk::IOComponentEnum::USHORT) || + (inputPixelType.GetComponentType() == itk::IOComponentEnum::FLOAT) || + (inputPixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE))) { return true; } return false; } void mitk::RGBToRGBACastImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image *output = this->GetOutput(); mitk::Image *input = this->GetInput(); if (!output->IsInitialized()) { return; } input->SetRequestedRegionToLargestPossibleRegion(); // GenerateTimeInInputRegion(output, input); } void mitk::RGBToRGBACastImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); // Initialize RGBA output with same pixel type as input image const mitk::PixelType &inputPixelType = input->GetPixelType(); typedef itk::Image UCRGBItkImageType; typedef itk::Image USRGBItkImageType; typedef itk::Image FloatCRGBItkImageType; typedef itk::Image DoubleRGBItkImageType; if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } void mitk::RGBToRGBACastImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (!output->IsInitialized()) { return; } m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); TimePointType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *image = m_InputTimeSelector->GetOutput(); const mitk::PixelType &pixelType = image->GetPixelType(); // Check if the pixel type is supported if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (UCRGBPixelType), this, 255); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (USRGBPixelType), this, 65535); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (FloatRGBPixelType), this, 1.0); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (DoubleRGBPixelType), this, 1.0); } else { // Otherwise, write warning and graft input to output // ...TBD... } } m_TimeOfHeaderInitialization.Modified(); } template void mitk::RGBToRGBACastImageFilter::InternalCast(itk::Image *inputItkImage, mitk::RGBToRGBACastImageFilter *addComponentFilter, typename TPixel::ComponentType defaultAlpha) { typedef TPixel InputPixelType; typedef itk::RGBAPixel OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ImageRegionConstIterator InputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex OutputImageIteratorType; typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(addComponentFilter->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename OutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename InputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); InputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); OutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { typename InputPixelType::Iterator pixelInputIt = inputIt.Get().Begin(); typename OutputPixelType::Iterator pixelOutputIt = outputIt.Get().Begin(); *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt = defaultAlpha; } } diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index 97c51890f6..244ed4dc3d 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,777 +1,776 @@ /*============================================================================ 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 "mitkRenderingManager.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProportionalTimeGeometry.h" #include "mitkRenderingManagerFactory.h" #include #include #include #include #include "mitkNumericTypes.h" -#include #include #include #include #include namespace mitk { itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent); RenderingManager::Pointer RenderingManager::s_Instance = nullptr; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr; RenderingManager::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(nullptr), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr), m_AntiAliasing(AntiAliasing::FastApproximate) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(nullptr); auto callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager::New() { const RenderingManagerFactory *factory = GetFactory(); if (factory == nullptr) return nullptr; return factory->CreateRenderingManager(); } RenderingManager *RenderingManager::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) mitk::BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(nullptr); // Add callbacks for rendering abort mechanism // BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; // Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { auto callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } auto rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(nullptr); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); baseRenderer->SetConstrainZoomingAndPanning(m_ConstrainedPanningZooming); // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { // prepare the camera etc. before rendering // Note: this is a very important step which should be called before the VTK render! // If you modify the camera anywhere else or after the render call, the scene cannot be seen. auto *vPR = dynamic_cast(baseRenderer); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage *ds) { if (!ds) return; // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(pred); // calculate bounding geometry of these nodes auto bounds = ds->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry this->InitializeViews(bounds); } // TODO_GOETZ // Remove old function, so only this one is working. bool RenderingManager::InitializeViews(const BaseGeometry *dataGeometry, RequestType type, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); return InitializeViews(propTimeGeometry, type, resetCamera); } bool RenderingManager::InitializeViews(const TimeGeometry *dataGeometry, RequestType type, bool resetCamera) { MITK_DEBUG << "initializing views"; bool boundingBoxInitialized = false; TimeGeometry::ConstPointer timeGeometry = dataGeometry; TimeGeometry::Pointer modifiedGeometry = nullptr; if (dataGeometry != nullptr) { modifiedGeometry = dataGeometry->Clone(); } int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((timeGeometry.IsNotNull()) && (timeGeometry->GetBoundingBoxInWorld()->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } if (timeGeometry.IsNotNull()) { // make sure bounding box has an extent bigger than zero in any direction // clone the input geometry // Old Geometry3D::Pointer modifiedGeometry = dynamic_cast( dataGeometry->Clone().GetPointer() ); assert(modifiedGeometry.IsNotNull()); for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { // check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if (Equal( newBounds[2 * dimension], newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision { mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers " "beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } } timeGeometry = modifiedGeometry; RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); int id = baseRenderer->GetMapperID(); if (((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))) { this->InternalViewInitialization(baseRenderer, timeGeometry, boundingBoxInitialized, id, resetCamera); } } if (boundingBoxInitialized) { m_TimeNavigationController->SetInputWorldTimeGeometry(timeGeometry); } m_TimeNavigationController->Update(); this->RequestUpdateAll(type); vtkObject::SetGlobalWarningDisplay(warningLevel); // Inform listeners that views have been initialized this->InvokeEvent(mitk::RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeViews(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); } } this->RequestUpdateAll(type); return true; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTimeSNC, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return InitializeView(renderWindow, propTimeGeometry, initializeGlobalTimeSNC, resetCamera); } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool initializeGlobalTimeSNC, bool resetCamera) { bool boundingBoxInitialized = false; int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((geometry != nullptr) && (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, geometry, boundingBoxInitialized, id, resetCamera); if (boundingBoxInitialized && initializeGlobalTimeSNC) { m_TimeNavigationController->SetInputWorldTimeGeometry(geometry); } m_TimeNavigationController->Update(); this->RequestUpdate(renderWindow); vtkObject::SetGlobalWarningDisplay(warningLevel); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); this->RequestUpdate(renderWindow); return true; } void RenderingManager::InternalViewInitialization(mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (resetCamera) { if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered in the image nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); baseRenderer->GetCameraController()->Fit(); } else if (mapperID == BaseRenderer::Standard3D) { baseRenderer->GetCameraController()->SetViewToAnterior(); } } } else { nc->Update(); } } const SliceNavigationController *RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController *RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) renderingManager->m_RenderWindowList[renderWindow] = RENDERING_INPROGRESS; renderingManager->m_UpdatePending = false; } void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); if (renderingManager->m_LODAbortMechanismEnabled) { auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) { auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr != renderer && 0 < renderer->GetNumberOfVisibleLODEnabledMappers()) renderingManager->DoMonitorRendering(); } } } void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) { auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr != renderer) { auto renderingManager = RenderingManager::GetInstance(); renderingManager->m_RenderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; if (0 < renderer->GetNumberOfVisibleLODEnabledMappers()) { if (0 == renderingManager->m_NextLODMap[renderer]) { renderingManager->StartOrResetTimer(); } else { renderingManager->m_NextLODMap[renderer] = 0; } } } } } bool RenderingManager::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager::GetNextLOD(BaseRenderer *renderer) { if (renderer != nullptr) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } // enable/disable shading void RenderingManager::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } // enable/disable the clipping plane void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage *storage) { if (storage != nullptr) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { mitk::BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } mitk::DataStorage *RenderingManager::GetDataStorage() { return m_DataStorage; } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (focusWindow != m_FocusedRenderWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } } void RenderingManager::SetAntiAliasing(AntiAliasing antiAliasing) { if (m_AntiAliasing != antiAliasing) { auto renderingManager = mitk::RenderingManager::GetInstance(); auto renderWindows = renderingManager->GetAllRegisteredRenderWindows(); for (auto renderWindow : renderWindows) { auto renderers = renderWindow->GetRenderers(); if (nullptr != renderers) { renderers->InitTraversal(); auto renderer = renderers->GetNextItem(); while (nullptr != renderer) { renderer->SetUseFXAA(AntiAliasing::FastApproximate == antiAliasing); renderer = renderers->GetNextItem(); } renderingManager->RequestUpdate(renderWindow); } } m_AntiAliasing = antiAliasing; } } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp index a859544e35..31eb0ac623 100644 --- a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp @@ -1,330 +1,332 @@ /*============================================================================ 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 "mitkAbstractTransformGeometry.h" #include mitk::AbstractTransformGeometry::AbstractTransformGeometry() : Superclass(), m_Plane(nullptr), m_FrameGeometry(nullptr) { Initialize(); m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry &other) : Superclass(other), m_ParametricBoundingBox(other.m_ParametricBoundingBox) { if (other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); this->SetParametricBounds(m_ParametricBoundingBox->GetBounds()); } this->SetPlane(other.m_Plane); this->SetFrameGeometry(other.m_FrameGeometry); m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } mitk::AbstractTransformGeometry::~AbstractTransformGeometry() { } vtkAbstractTransform *mitk::AbstractTransformGeometry::GetVtkAbstractTransform() const { return m_ItkVtkAbstractTransform->GetVtkAbstractTransform(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtentInMM(int direction) const { if (m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is nullptr."); } return m_Plane->GetExtentInMM(direction); } const itk::Transform *mitk::AbstractTransformGeometry::GetParametricTransform() const { return m_ItkVtkAbstractTransform; } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); mitk::Point2D pt2d_mm; bool isInside; isInside = Map(pt3d_mm, pt2d_mm); Map(pt2d_mm, projectedPt3d_mm); return isInside; // Point3D pt3d_units; // pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); // pt3d_units[2] = 0; // projectedPt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_units); // return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_units; pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); return m_Plane->Map(pt3d_units, pt2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); m_Plane->Map(pt2d_mm, pt3d_mm); pt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_mm); } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by " "m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); assert(this->IsBoundingBoxNull() == false); - Vector3D vec3d_units; - vec3d_units = GetIndexToWorldTransform()->GetInverseMatrix() * vec3d_mm; + auto inverse = mitk::AffineTransform3D::New(); + GetIndexToWorldTransform()->GetInverse(inverse); + + Vector3D vec3d_units = inverse->GetMatrix() * vec3d_mm; vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; mitk::ScalarType temp[3]; unsigned int i, j; for (j = 0; j < 3; ++j) temp[j] = atPt3d_mm[j] - GetIndexToWorldTransform()->GetOffset()[j]; for (i = 0; i < 3; ++i) { pt3d_units[i] = 0.0; for (j = 0; j < 3; ++j) - pt3d_units[i] += GetIndexToWorldTransform()->GetInverseMatrix()[i][j] * temp[j]; + pt3d_units[i] += inverse->GetMatrix()[i][j] * temp[j]; } return this->GetBoundingBox()->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Project(const mitk::Vector3D & /*vec3d_mm*/, mitk::Vector3D & /*projectedVec3d_mm*/) const { MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, " "const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not " "implemented at the moment. Sorry :("; itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by " "m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); return false; } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); ScalarType vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::Vector3D vec3d_units; vtk2itk(vtkvec, vec3d_units); return m_Plane->Map(atPt3d_mm, vec3d_units, vec2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const { m_Plane->Map(atPt2d_mm, vec2d_mm, vec3d_mm); Point3D atPt3d_mm; Map(atPt2d_mm, atPt3d_mm); float vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); vtk2itk(vtkvec, vec3d_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const { m_Plane->IndexToWorld(pt_units, pt_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const { m_Plane->WorldToIndex(pt_mm, pt_units); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D & /*atPt2d_units*/, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::IndexToWorld(point, vec, vec). Use " "AbstractTransformGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { m_Plane->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::WorldToIndex(point, vec, vec). Use " "AbstractTransformGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { m_Plane->WorldToIndex(vec_mm, vec_units); } bool mitk::AbstractTransformGeometry::IsAbove(const mitk::Point3D &pt3d_mm, bool /*considerBoundingBox*/) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_ParametricWorld; pt3d_ParametricWorld = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); Point3D pt3d_ParametricUnits; ((BaseGeometry *)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits); return (pt3d_ParametricUnits[2] > m_ParametricBoundingBox->GetBounds()[4]); } void mitk::AbstractTransformGeometry::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform) { m_ItkVtkAbstractTransform->SetVtkAbstractTransform(aVtkAbstractTransform); } void mitk::AbstractTransformGeometry::SetPlane(const mitk::PlaneGeometry *aPlane) { if (aPlane != nullptr) { m_Plane = static_cast(aPlane->Clone().GetPointer()); BoundingBox::BoundsArrayType b = m_Plane->GetBoundingBox()->GetBounds(); SetParametricBounds(b); CalculateFrameGeometry(); } else { if (m_Plane.IsNull()) return; m_Plane = nullptr; } Modified(); } void mitk::AbstractTransformGeometry::CalculateFrameGeometry() { if ((m_Plane.IsNull()) || (m_FrameGeometry.IsNotNull())) return; //@warning affine-transforms and bounding-box should be set by specific sub-classes! SetBounds(m_Plane->GetBoundingBox()->GetBounds()); } void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::BaseGeometry *frameGeometry) { if ((frameGeometry != nullptr) && (frameGeometry->IsValid())) { m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform()); SetBounds(m_FrameGeometry->GetBounds()); } else { m_FrameGeometry = nullptr; } } -unsigned long mitk::AbstractTransformGeometry::GetMTime() const +itk::ModifiedTimeType mitk::AbstractTransformGeometry::GetMTime() const { if (Superclass::GetMTime() < m_ItkVtkAbstractTransform->GetMTime()) return m_ItkVtkAbstractTransform->GetMTime(); return Superclass::GetMTime(); } void mitk::AbstractTransformGeometry::SetOversampling(mitk::ScalarType oversampling) { if (m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is not set."); } mitk::BoundingBox::BoundsArrayType bounds = m_Plane->GetBounds(); bounds[1] *= oversampling; bounds[3] *= oversampling; bounds[5] *= oversampling; SetParametricBounds(bounds); } itk::LightObject::Pointer mitk::AbstractTransformGeometry::InternalClone() const { Self::Pointer newGeometry = new AbstractTransformGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::AbstractTransformGeometry::SetParametricBounds(const BoundingBox::BoundsArrayType &bounds) { m_ParametricBoundingBox = 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 < GetNDimensions(); ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_ParametricBoundingBox->SetPoints(pointscontainer); m_ParametricBoundingBox->ComputeBoundingBox(); this->Modified(); } const mitk::BoundingBox::BoundsArrayType &mitk::AbstractTransformGeometry::GetParametricBounds() const { assert(m_ParametricBoundingBox.IsNotNull()); return m_ParametricBoundingBox->GetBounds(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtent(int direction) const { if (direction < 0 || direction >= 3) mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. "; assert(m_ParametricBoundingBox.IsNotNull()); BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } diff --git a/Modules/Core/src/DataManagement/mitkBaseData.cpp b/Modules/Core/src/DataManagement/mitkBaseData.cpp index 9f3b670884..3750bd4848 100644 --- a/Modules/Core/src/DataManagement/mitkBaseData.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseData.cpp @@ -1,348 +1,348 @@ /*============================================================================ 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 "mitkBaseData.h" #include #include #include #include #include mitk::BaseData::BaseData() : m_SourceOutputIndexDuplicate(0), m_Initialized(true), m_PropertyList(PropertyList::New()), m_TimeGeometry(ProportionalTimeGeometry::New()) { } mitk::BaseData::BaseData(const BaseData &other) : itk::DataObject(), OperationActor(), Identifiable(), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), m_Initialized(other.m_Initialized), m_PropertyList(other.m_PropertyList->Clone()), m_TimeGeometry(other.m_TimeGeometry->Clone()) { } mitk::BaseData::~BaseData() { } void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps) { mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New(); mitk::BaseGeometry::Pointer baseGeo = dynamic_cast(geo3D.GetPointer()); baseGeo->Initialize(); // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... // Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry(); timeGeometry->Initialize(); timeGeometry->Expand(timeSteps); for (TimeStepType step = 0; step < timeSteps; ++step) { timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(), step); } } void mitk::BaseData::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->UpdateBoundingBox(); } } const mitk::TimeGeometry *mitk::BaseData::GetUpdatedTimeGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeGeometry(); } void mitk::BaseData::Expand(unsigned int timeSteps) { if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->Expand(timeSteps); } else { this->InitializeTimeGeometry(timeSteps); } } const mitk::BaseGeometry *mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } void mitk::BaseData::SetGeometry(BaseGeometry *geometry) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); if (geometry != nullptr) { timeGeometry->Initialize(geometry, 1); } SetTimeGeometry(timeGeometry); return; } void mitk::BaseData::SetTimeGeometry(TimeGeometry *geometry) { m_TimeGeometry = geometry; this->Modified(); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry *geometry) { TimeGeometry::Pointer clonedGeometry = geometry->Clone(); SetTimeGeometry(clonedGeometry.GetPointer()); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time) { if (m_TimeGeometry) { m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()), time); } } bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if (IsInitialized() == false) return true; const TimeGeometry *timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if (timeGeometry == nullptr) return true; unsigned int timeSteps = timeGeometry->CountTimeSteps(); for (unsigned int t = 0; t < timeSteps; ++t) { if (IsEmptyTimeStep(t) == false) return false; } return true; } itk::SmartPointer mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const { return m_PropertyList; } mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::BaseData::SetPropertyList(PropertyList *pList) { m_PropertyList = pList; } void mitk::BaseData::SetOrigin(const mitk::Point3D &origin) { TimeGeometry *timeGeom = GetTimeGeometry(); assert(timeGeom != nullptr); TimeStepType steps = timeGeom->CountTimeSteps(); for (TimeStepType timestep = 0; timestep < steps; ++timestep) { auto geometry = GetGeometry(timestep); if (geometry != nullptr) { geometry->SetOrigin(origin); } } } -unsigned long mitk::BaseData::GetMTime() const +itk::ModifiedTimeType mitk::BaseData::GetMTime() const { - unsigned long time = Superclass::GetMTime(); + auto time = Superclass::GetMTime(); if (m_TimeGeometry.IsNotNull()) { if ((time < m_TimeGeometry->GetMTime())) { return m_TimeGeometry->GetMTime(); } } return time; } void mitk::BaseData::Graft(const itk::DataObject *) { itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass()) } void mitk::BaseData::CopyInformation(const itk::DataObject *data) { const auto *bd = dynamic_cast(data); if (bd != nullptr) { m_PropertyList = bd->GetPropertyList()->Clone(); if (bd->GetTimeGeometry() != nullptr) { m_TimeGeometry = bd->GetTimeGeometry()->Clone(); } } else { // pointer could not be cast back down; this can be the case if your filters input // and output objects differ in type; then you have to write your own GenerateOutputInformation method itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self *).name()); } } bool mitk::BaseData::IsInitialized() const { return m_Initialized; } void mitk::BaseData::Clear() { this->ClearData(); this->InitializeEmpty(); } void mitk::BaseData::ClearData() { if (m_Initialized) { ReleaseData(); m_Initialized = false; } } void mitk::BaseData::ExecuteOperation(mitk::Operation * /*operation*/) { // empty by default. override if needed! } void mitk::BaseData::PrintSelf(std::ostream &os, itk::Indent indent) const { os << std::endl; os << indent << " TimeGeometry: "; if (GetTimeGeometry() == nullptr) os << "nullptr" << std::endl; else GetTimeGeometry()->Print(os, indent); // print out all properties PropertyList::Pointer propertyList = this->GetPropertyList(); if (propertyList.IsNotNull() && !propertyList->IsEmpty()) { // general headline os << "Properties of BaseData:" << std::endl; const PropertyList::PropertyMap *map = propertyList->GetMap(); for (auto iter = map->begin(); iter != map->end(); ++iter) { os << " " << (*iter).first << " " << (*iter).second->GetValueAsString() << std::endl; } } } mitk::BaseProperty::ConstPointer mitk::BaseData::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const { if (propertyKey.empty()) return nullptr; if (contextName.empty() || fallBackOnDefaultContext) return m_PropertyList->GetProperty(propertyKey); return nullptr; } mitk::BaseProperty * mitk::BaseData::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) return nullptr; if (contextName.empty() || fallBackOnDefaultContext) return m_PropertyList->GetProperty(propertyKey); return nullptr; } void mitk::BaseData::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->SetProperty(propertyKey, property); return; } mitkThrow() << "Unknown or unsupported non-default property context."; } void mitk::BaseData::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->RemoveProperty(propertyKey); return; } mitkThrow() << "Unknown or unsupported non-default property context."; } std::vector mitk::BaseData::GetPropertyKeys(const std::string &/*contextName*/, bool /*includeDefaultContext*/) const { return m_PropertyList->GetPropertyKeys(); } std::vector mitk::BaseData::GetPropertyContextNames() const { return std::vector(); } diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index f1323caf3f..d36e5af0e4 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1097 +1,1101 @@ /*============================================================================ 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 = Point3D(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++) + auto inverse = mitk::AffineTransform3D::New(); + if (this->GetIndexToWorldTransform()->GetInverse(inverse)) { - os << indent.GetNextIndent(); - for (j = 0; j < 3; j++) + os << indent << "Inverse: " << std::endl; + for (i = 0; i < 3; i++) { - os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; + os << indent.GetNextIndent(); + for (j = 0; j < 3; j++) + { + os << inverse->GetMatrix()[i][j] << " "; + } + os << std::endl; } - 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) { 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 coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings 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 " << coordinateEps; } result = false; } // Compare Origins 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 " << coordinateEps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { 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 " << directionEps; } result = false; } 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 " << 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(), coordinateEps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool 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 coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings (must be equal) const auto testedSpacing = testGeo.GetSpacing(); 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 " << 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()), directionEps, verbose)) { result = false; } //check if the geometry is within or equal to the bounds of reference geomentry. for (int i = 0; i<8; ++i) { auto testCorner = testGeo.GetCornerPoint(i); bool isInside = false; mitk::Point3D testCornerIndex; referenceGeo.WorldToIndex(testCorner, testCornerIndex); std::bitset bs(i); //To regard the coordinateEps, we substract or add it to the index elements //depending on wether it was constructed by a lower or an upper bound value //(see implementation of BaseGeometry::GetCorner()). if (bs.test(0)) { testCornerIndex[2] -= coordinateEps; } else { testCornerIndex[2] += coordinateEps; } if (bs.test(1)) { testCornerIndex[1] -= coordinateEps; } else { testCornerIndex[1] += coordinateEps; } if (bs.test(2)) { testCornerIndex[0] -= coordinateEps; } else { testCornerIndex[0] += coordinateEps; } isInside = referenceGeo.IsIndexInside(testCornerIndex); if (!isInside) { 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, due to equal transfor (see above)) 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); mitk::Vector3D cornerIndexOffset; referenceGeo.WorldToIndex(cornerOffset, cornerIndexOffset); for (unsigned int i = 0; i < 3; ++i) { auto pixelCountContinous = cornerIndexOffset[i]; auto pixelCount = std::round(pixelCountContinous); 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 "< #include "mitkGroupTagProperty.h" #include "mitkProperties.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" //#include "mitkMaterialProperty.h" #include "mitkColorProperty.h" #include "mitkCoreObjectFactory.h" #include "mitkGenericProperty.h" #include "mitkGeometry3D.h" #include "mitkImageSource.h" #include "mitkLevelWindowProperty.h" #include "mitkRenderingManager.h" mitk::Mapper *mitk::DataNode::GetMapper(MapperSlotId id) const { if ((id >= m_Mappers.size()) || (m_Mappers[id].IsNull())) { if (id >= m_Mappers.capacity()) { // int i, size=id-m_Mappers.capacity()+10; m_Mappers.resize(id + 10); } m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast(this), id); } return m_Mappers[id]; } mitk::BaseData *mitk::DataNode::GetData() const { return m_Data; } void mitk::DataNode::SetData(mitk::BaseData *baseData) { if (m_Data != baseData) { m_Mappers.clear(); m_Mappers.resize(10); if (m_Data.IsNotNull() && baseData != nullptr) { // Do previous and new data have same type? Keep existing properties. if (0 == strcmp(m_Data->GetNameOfClass(), baseData->GetNameOfClass())) { m_Data = baseData; } else { m_Data = baseData; this->GetPropertyList()->Clear(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } } else { m_Data = baseData; mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } m_DataReferenceChangedTime.Modified(); Modified(); } } mitk::DataNode::DataNode() : m_PropertyList(PropertyList::New()), m_PropertyListModifiedObserverTag(0) { m_Mappers.resize(10); // subscribe for modified event itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); } mitk::DataNode::~DataNode() { if (m_PropertyList.IsNotNull()) m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag); m_Mappers.clear(); m_Data = nullptr; } mitk::DataNode &mitk::DataNode::operator=(const DataNode &right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right.GetData()); return *node; } mitk::DataNode &mitk::DataNode::operator=(mitk::BaseData *right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right); return *node; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) std::istream &mitk::operator>>(std::istream &i, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) std::istream & operator>>(std::istream &i, mitk::DataNode::Pointer &dtn) #endif { dtn = mitk::DataNode::New(); // i >> av.get(); return i; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) std::ostream &mitk::operator<<(std::ostream &o, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) std::ostream & operator<<(std::ostream &o, mitk::DataNode::Pointer &dtn) #endif { if (dtn->GetData() != nullptr) o << dtn->GetData()->GetNameOfClass(); else o << "empty data"; return o; } void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper *mapper) { m_Mappers[id] = mapper; if (mapper != nullptr) mapper->SetDataNode(this); } void mitk::DataNode::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } } void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::DataNode::VerifyRequestedRegion() { return true; } void mitk::DataNode::SetRequestedRegion(const itk::DataObject * /*data*/) { } mitk::DataNode::PropertyListKeyNames mitk::DataNode::GetPropertyListNames() const { PropertyListKeyNames result; for (const auto &entries : m_MapOfPropertyLists) result.push_back(entries.first); return result; } void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/) { } mitk::PropertyList *mitk::DataNode::GetPropertyList(const mitk::BaseRenderer *renderer) const { if (renderer == nullptr) return m_PropertyList; return this->GetPropertyList(renderer->GetName()); } mitk::PropertyList *mitk::DataNode::GetPropertyList(const std::string &rendererName) const { if (rendererName.empty()) return m_PropertyList; mitk::PropertyList::Pointer &propertyList = m_MapOfPropertyLists[rendererName]; if (propertyList.IsNull()) propertyList = mitk::PropertyList::New(); assert(m_MapOfPropertyLists[rendererName].IsNotNull()); return propertyList; } void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer, bool fallBackOnDataProperties) const { if (nullptr == propertyKey) return nullptr; if (nullptr != renderer) { auto it = m_MapOfPropertyLists.find(renderer->GetName()); if (m_MapOfPropertyLists.end() != it) { auto property = it->second->GetProperty(propertyKey); if (nullptr != property) return property; } } auto property = m_PropertyList->GetProperty(propertyKey); if (nullptr == property && fallBackOnDataProperties && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey); return property; } mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const { GroupTagList groups; const PropertyList::PropertyMap *propertyMap = m_PropertyList->GetMap(); for (auto groupIter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here groupIter != propertyMap->end(); ++groupIter) { const BaseProperty *bp = groupIter->second; if (dynamic_cast(bp)) { groups.insert(groupIter->first); } } return groups; } bool mitk::DataNode::GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::DataNode::GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::DataNode::GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::DataNode::GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer) const { mitk::DoubleProperty::Pointer doubleprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (doubleprop.IsNull()) { // try float instead float floatValue = 0; if (this->GetFloatProperty(propertyKey, floatValue, renderer)) { doubleValue = floatValue; return true; } return false; } doubleValue = doubleprop->GetValue(); return true; } bool mitk::DataNode::GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } bool mitk::DataNode::GetColor(float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } bool mitk::DataNode::GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::LevelWindowProperty::Pointer levWinProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (levWinProp.IsNull()) return false; levelWindow = levWinProp->GetLevelWindow(); return true; } void mitk::DataNode::SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetColor( float red, float green, float blue, const mitk::BaseRenderer *renderer, const char *propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, renderer, propertyKey); } void mitk::DataNode::SetColor(const float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetVisibility(bool visible, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetOpacity(float opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::LevelWindowProperty::Pointer prop; prop = mitk::LevelWindowProperty::New(levelWindow); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::DataNode::SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::DataNode::SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting float property " << propertyKey << " although a double property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::DataNode::SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting double property " << propertyKey << " although a float property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } void mitk::DataNode::SetStringProperty(const char *propertyKey, const char *stringValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::DataNode::SetProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue); } void mitk::DataNode::ReplaceProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue); } void mitk::DataNode::AddProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey, renderer) == nullptr)) { SetProperty(propertyKey, propertyValue, renderer); } } vtkLinearTransform *mitk::DataNode::GetVtkTransform(int t) const { assert(m_Data.IsNotNull()); mitk::BaseGeometry *geometry = m_Data->GetGeometry(t); if (geometry == nullptr) return nullptr; return geometry->GetVtkTransform(); } -unsigned long mitk::DataNode::GetMTime() const +itk::ModifiedTimeType mitk::DataNode::GetMTime() const { - unsigned long time = Superclass::GetMTime(); + auto time = Superclass::GetMTime(); if (m_Data.IsNotNull()) { if ((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime()))) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::DataNode::SetSelected(bool selected, const mitk::BaseRenderer *renderer) { mitk::BoolProperty::Pointer selectedProperty = dynamic_cast(GetProperty("selected")); if (selectedProperty.IsNull()) { selectedProperty = mitk::BoolProperty::New(); selectedProperty->SetValue(false); SetProperty("selected", selectedProperty, renderer); } if (selectedProperty->GetValue() != selected) { selectedProperty->SetValue(selected); itk::ModifiedEvent event; InvokeEvent(event); } } /* class SelectedEvent : public itk::ModifiedEvent { public: typedef SelectedEvent Self; typedef itk::ModifiedEvent Superclass; SelectedEvent(DataNode* dataNode) { m_DataNode = dataNode; }; DataNode* GetDataNode() { return m_DataNode; }; virtual const char * GetEventName() const { return "SelectedEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_DataNode); } private: DataNode* m_DataNode; SelectedEvent(const Self& event) { m_DataNode = event.m_DataNode; }; void operator=(const Self& event) { m_DataNode = event.m_DataNode; } }; */ bool mitk::DataNode::IsSelected(const mitk::BaseRenderer *renderer) { bool selected; if (!GetBoolProperty("selected", selected, renderer)) return false; return selected; } void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer interactor) { if (m_DataInteractor == interactor) return; m_DataInteractor = interactor; this->Modified(); mitk::DataNode::InteractorChangedEvent changedEvent; this->InvokeEvent(changedEvent); } mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const { return m_DataInteractor; } void mitk::DataNode::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { Modified(); } mitk::BaseProperty::ConstPointer mitk::DataNode::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const { if (propertyKey.empty()) return nullptr; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { BaseProperty::ConstPointer property = propertyListIter->second->GetProperty(propertyKey); if (property.IsNotNull()) return property; } } if (contextName.empty() || fallBackOnDefaultContext) { BaseProperty::ConstPointer property = m_PropertyList->GetProperty(propertyKey); if (property.IsNull() && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey.c_str()); return property; } return nullptr; } mitk::BaseProperty * mitk::DataNode::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) return nullptr; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { auto property = propertyListIter->second->GetProperty(propertyKey); if (nullptr != property) return property; } } if (contextName.empty() || fallBackOnDefaultContext) { auto property = m_PropertyList->GetProperty(propertyKey); if (nullptr == property && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey.c_str()); return property; } return nullptr; } void mitk::DataNode::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { propertyListIter->second->SetProperty(propertyKey, property); return; } } if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->SetProperty(propertyKey, property); return; } mitkThrow() << "Unknown property context."; } void mitk::DataNode::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { propertyListIter->second->RemoveProperty(propertyKey); return; } } if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->RemoveProperty(propertyKey); return; } mitkThrow() << "Unknown property context."; } std::vector mitk::DataNode::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty()) { for (const auto &property : *m_PropertyList->GetMap()) propertyKeys.push_back(property.first); return propertyKeys; } auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { for (const auto &property : *propertyListIter->second->GetMap()) propertyKeys.push_back(property.first); } if (includeDefaultContext) { for (const auto &property : *m_PropertyList->GetMap()) { auto propertyKeyIter = std::find(propertyKeys.begin(), propertyKeys.end(), property.first); if (propertyKeys.end() == propertyKeyIter) propertyKeys.push_back(property.first); } } return propertyKeys; } std::vector mitk::DataNode::GetPropertyContextNames() const { return this->GetPropertyListNames(); } diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp index 5a555a15da..28a442ef78 100644 --- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp @@ -1,584 +1,583 @@ /*============================================================================ 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 "mitkDataStorage.h" #include "itkCommand.h" -#include "itkMutexLockHolder.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkArbitraryTimeGeometry.h" mitk::DataStorage::DataStorage() : itk::Object(), m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass // SetOfObjects::ConstPointer all = this->GetAll(); // for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); // m_NodeModifiedObserverTags.clear(); // m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(DataNode *node, DataNode *parent) { DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); if (parent != nullptr) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const DataStorage::SetOfObjects *nodes) { if (nodes == nullptr) return; for (DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const { DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const { if (condition == nullptr) return nullptr; DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os, indent); DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != nullptr) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const { if (set == nullptr) return nullptr; DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); for (DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == nullptr || condition->CheckNode(it.Value()) == true) // alway copy the set, otherwise the iterator in DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { PropertyList *pl = nodeIt.Value()->GetPropertyList(); for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) if (dynamic_cast(propIt->second.GetPointer()) != nullptr) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const DataNode *node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const DataNode *node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &) { const auto *_Node = dynamic_cast(caller); if (_Node) { InteractorChangedNodeEvent.Send(_Node); } } void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event) { if (m_BlockNodeModifiedEvents) return; const auto *_Node = dynamic_cast(caller); if (_Node) { const auto *modEvent = dynamic_cast(&event); if (modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners(const DataNode *_Node) { - itk::MutexLockHolder locked(m_MutexOne); + std::lock_guard locked(m_MutexOne); // node must not be 0 and must not be yet registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver(DataNode::InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners(const DataNode *_Node) { - itk::MutexLockHolder locked(m_MutexOne); + std::lock_guard locked(m_MutexOne); // node must not be 0 and must be registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { if (input == nullptr) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; Vector3D minSpacing; minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmax = itk::NumericTraits::max(); ScalarType stmin = itk::NumericTraits::NonpositiveMin(); std::set existingTimePoints; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++) { // We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing // in its original space, which, in case of an image geometry, can have the values in different // order than in world space. For the further calculations, we need to have the spacing values // in world coordinate order (sag-cor-ax). Vector3D spacing; spacing.Fill(1.0); node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing); for (int axis = 0; axis < 3; ++ axis) { ScalarType space = std::abs(spacing[axis]); if (space < minSpacing[axis]) { minSpacing[axis] = space; } } const auto curTimeBounds = timeGeometry->GetTimeBounds(i); if ((curTimeBounds[0] > stmin) && (curTimeBounds[0] < stmax)) { existingTimePoints.insert(curTimeBounds[0]); } if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax)) { maximalTime = curTimeBounds[1]; } } } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << e.GetDescription() << std::endl; } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps if (existingTimePoints.empty()) // make sure that there is at least one time sliced geometry in the data storage { existingTimePoints.insert(0.0); maximalTime = 1.0; } ArbitraryTimeGeometry::Pointer timeGeometry = nullptr; if (result->GetPoints()->Size() > 0) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); AffineTransform3D::OutputVectorType offset; for (int i = 0; i < 3; ++i) { offset[i] = bounds[i * 2]; bounds[i * 2] = 0.0; bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i]; } geometry->GetIndexToWorldTransform()->SetOffset(offset); geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry auto tsIterator = existingTimePoints.cbegin(); auto tsPredecessor = tsIterator++; auto tsEnd = existingTimePoints.cend(); timeGeometry = ArbitraryTimeGeometry::New(); for (; tsIterator != tsEnd; ++tsIterator, ++tsPredecessor) { timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, *tsIterator); } timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, maximalTime); timeGeometry->Update(); } return timeGeometry.GetPointer(); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = geometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin = itk::NumericTraits::NonpositiveMin(); stmax = itk::NumericTraits::max(); timeBounds[0] = stmax; timeBounds[1] = stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { const TimeBounds &curTimeBounds = geometry->GetTimeBounds(); cur = curTimeBounds[0]; // is it after -infinity, but before everything else that we found until now? if ((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur = curTimeBounds[1]; // is it before infinity, but after everything else that we found until now? if ((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if (!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents(bool block) { m_BlockNodeModifiedEvents = block; } mitk::DataNode::Pointer mitk::FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer* baseRender) { if (nodes.IsNull()) { return nullptr; } mitk::DataNode::Pointer topLayerNode = nullptr; int maxLayer = std::numeric_limits::min(); for (const auto &node : *nodes) { if (node.IsNull()) { continue; } bool isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { continue; } auto data = node->GetData(); if (nullptr == data) { continue; } auto geometry = data->GetGeometry(); if (nullptr == geometry || !geometry->IsInside(worldPosition)) { continue; } auto timeGeometry = data->GetUpdatedTimeGeometry(); if (nullptr == timeGeometry) { continue; } if (!timeGeometry->IsValidTimePoint(timePoint)) { continue; } int layer = 0; if (!node->GetIntProperty("layer", layer, baseRender)) { continue; } if (layer <= maxLayer) { continue; } if (!node->IsVisible(baseRender)) { continue; } topLayerNode = node; maxLayer = layer; } return topLayerNode; } diff --git a/Modules/Core/src/DataManagement/mitkImage.cpp b/Modules/Core/src/DataManagement/mitkImage.cpp index bd5ba5e95d..0c10c1302a 100644 --- a/Modules/Core/src/DataManagement/mitkImage.cpp +++ b/Modules/Core/src/DataManagement/mitkImage.cpp @@ -1,1358 +1,1355 @@ /*============================================================================ 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. ============================================================================*/ // MITK #include "mitkImage.h" #include "mitkCompareImageDataFilter.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" #include "mitkPixelTypeMultiplex.h" #include // VTK #include -// ITK -#include - // Other #include #define FILL_C_ARRAY(_arr, _size, _value) \ for (unsigned int i = 0u; i < _size; i++) \ \ { \ _arr[i] = _value; \ } mitk::Image::Image() : m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize(other.GetPixelType(), other.GetDimension(), other.GetDimensions()); // Since the above called "Initialize" method doesn't take the geometry into account we need to set it // here manually TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = other.GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = other.GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { this->Clear(); m_ReferenceCount = 3; m_ReferenceCount = 0; delete[] m_OffsetTable; delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if ((i >= 0) && (i < (int)m_Dimension)) return m_Dimensions[i]; return 1; } template void AccessPixel(const mitk::PixelType ptype, void *data, const unsigned int offset, double &value) { value = 0.0; if (data == nullptr) return; if (ptype.GetBpe() != 24) { value = (double)(((T *)data)[offset]); } else { const unsigned int rgboffset = offset; double returnvalue = (((T *)data)[rgboffset]); returnvalue += (((T *)data)[rgboffset + 1]); returnvalue += (((T *)data)[rgboffset + 2]); value = returnvalue; } } vtkImageData *mitk::Image::GetVtkImageData(int t, int n) { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } const vtkImageData *mitk::Image::GetVtkImageData(int t, int n) const { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidSlice(s, t, n) == false) return nullptr; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos = GetSliceIndex(s, t, n); if (m_Slices[pos].GetPointer() != nullptr) { return m_Slices[pos]; } // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // slice is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsSliceSet_unlocked(s, t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidVolume(t, n) == false) return nullptr; ImageDataItemPointer ch, vol; // volume directly available? int pos = GetVolumeIndex(t, n); vol = m_Volumes[pos]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); vol->SetComplete(true); return m_Volumes[pos] = vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete = true; unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { complete = false; break; } } if (complete) { // if there is only single slice we do not need to combine anything if (m_Dimensions[2] <= 1) { ImageDataItemPointer sl; sl = GetSliceData_unlocked(0, t, n, data, importMemoryManagement); vol = new ImageDataItem(*sl, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory); vol->SetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol = m_Volumes[pos]; // ok, let's combine the slices! if (vol.GetPointer() == nullptr) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); } vol->SetComplete(true); size_t size = m_OffsetTable[2] * (ptypeSize); for (s = 0; s < m_Dimensions[2]; ++s) { int posSl; ImageDataItemPointer sl; posSl = GetSliceIndex(s, t, n); sl = m_Slices[posSl]; if (sl->GetParent() != vol) { // copy data of slices in volume size_t offset = ((size_t)s) * size; std::memcpy(static_cast(vol->GetData()) + offset, sl->GetData(), size); // replace old slice with reference to volume sl = new ImageDataItem( *vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * size); sl->SetComplete(true); m_Slices[posSl] = sl; } } } return m_Volumes[pos] = vol; } // volume is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsVolumeSet_unlocked(t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData_unlocked(t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidChannel(n) == false) return nullptr; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if (IsChannelSet_unlocked(n)) { // if there is only one time frame we do not need to combine anything if (m_Dimensions[3] <= 1) { vol = GetVolumeData_unlocked(0, n, data, importMemoryManagement); ch = new ImageDataItem(*vol, m_ImageDescriptor, 0, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = m_Channels[n]; // ok, let's combine the volumes! if (ch.GetPointer() == nullptr) ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); ch->SetComplete(true); size_t size = m_OffsetTable[m_Dimension - 1] * (ptypeSize); unsigned int t; auto slicesIt = m_Slices.begin() + n * m_Dimensions[2] * m_Dimensions[3]; for (t = 0; t < m_Dimensions[3]; ++t) { int posVol; ImageDataItemPointer vol; posVol = GetVolumeIndex(t, n); vol = GetVolumeData_unlocked(t, n, data, importMemoryManagement); if (vol->GetParent() != ch) { // copy data of volume in channel size_t offset = ((size_t)t) * m_OffsetTable[3] * (ptypeSize); std::memcpy(static_cast(ch->GetData()) + offset, vol->GetData(), size); // replace old volume with reference to channel vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); m_Volumes[posVol] = vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull = nullptr; for (unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } } return m_Channels[n] = ch; } // channel is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); // did it work? if (IsChannelSet_unlocked(n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData_unlocked(n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateChannelData_unlocked(n, data, importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsSliceSet_unlocked(s, t, n); } bool mitk::Image::IsSliceSet_unlocked(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() != nullptr) { return true; } ImageDataItemPointer ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { return true; } ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { return true; } return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsVolumeSet_unlocked(t, n); } bool mitk::Image::IsVolumeSet_unlocked(int t, int n) const { if (IsValidVolume(t, n) == false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { return false; } } return true; } bool mitk::Image::IsChannelSet(int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsChannelSet_unlocked(n); } bool mitk::Image::IsChannelSet_unlocked(int n) const { if (IsValidChannel(n) == false) return false; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for (t = 0; t < m_Dimensions[3]; ++t) { if (IsVolumeSet_unlocked(t, n) == false) { return false; } } return true; } bool mitk::Image::SetSlice(const void *data, int s, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportSlice(const_cast(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidSlice(s, t, n) == false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if (IsSliceSet(s, t, n)) { sl = GetSliceData(s, t, n, data, importMemoryManagement); if (sl->GetManageMemory() == false) { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; } if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); sl->Modified(); // we have changed the data: call Modified()! Modified(); } else { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); // we just added a missing slice, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidVolume(t, n) == false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if (IsVolumeSet(t, n)) { vol = GetVolumeData(t, n, data, importMemoryManagement); if (vol->GetManageMemory() == false) { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; } if (vol->GetData() != data) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); vol->Modified(); vol->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; if (vol->GetData() != data) { std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(vol->GetData()); // we just added a missing Volume, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(const void *const_data, int t, int n) { return this->SetImportVolume(const_cast(const_data), t, n, CopyMemory); } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidChannel(n) == false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if (IsChannelSet(n)) { ch = GetChannelData(n, data, importMemoryManagement); if (ch->GetManageMemory() == false) { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; } if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->Modified(); ch->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(ch->GetData()); // we just added a missing Channel, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for (it = m_Slices.begin(), end = m_Slices.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Volumes.begin(), end = m_Volumes.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Channels.begin(), end = m_Channels.end(); it != end; ++it) { (*it) = nullptr; } m_CompleteData = nullptr; if (m_ImageStatistics == nullptr) { m_ImageStatistics = new mitk::ImageStatisticsHolder(this); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1); } void mitk::Image::Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension = dimension; if (!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for (i = 0; i < dimension; ++i) { if (dimensions[i] < 1) itkExceptionMacro(<< "invalid dimension[" << i << "]: " << dimensions[i]); } // create new array since the old was deleted m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; // initialize the first four dimensions to 1, the remaining 4 to 0 FILL_C_ARRAY(m_Dimensions, 4, 1u); FILL_C_ARRAY((m_Dimensions + 4), 4, 0u); // copy in the passed dimension information std::memcpy(m_Dimensions, dimensions, sizeof(unsigned int) * m_Dimension); this->m_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize(this->m_Dimensions, this->m_Dimension); for (i = 0; i < 4; ++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if (m_LargestPossibleRegion.GetNumberOfPixels() == 0) { delete[] m_Dimensions; m_Dimensions = nullptr; return; } for (unsigned int i = 0u; i < channels; i++) { this->m_ImageDescriptor->AddNewChannel(type); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull = nullptr; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels() * m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels() * m_Dimensions[3] * m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels, int tDim) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry.Clone(), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels, int tDim) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0) + 0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1) + 0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2) + 0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if (dimensions[2] > 1) dimension = 3; if (dimensions[3] > 1) dimension = 4; Initialize(type, dimension, dimensions, channels); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); // make sure the image geometry flag is properly set for all time steps for (TimeStepType step = 0; step < cloned->CountTimeSteps(); ++step) { if (!cloned->GetGeometryCloneForTimeStep(step)->GetImageGeometry()) { MITK_WARN("Image.3DnT.Initialize") << " Attempt to initialize an image with a non-image geometry. " "Re-interpretting the initialization geometry for timestep " << step << " as image geometry, the original geometry remains unchanged."; cloned->GetGeometryForTimeStep(step)->ImageGeometryOn(); } } } else { // make sure the image geometry coming from outside has proper value of the image geometry flag BaseGeometry::Pointer cloned = geometry.GetGeometryCloneForTimeStep(0)->Clone(); if (!cloned->GetImageGeometry()) { MITK_WARN("Image.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting " "the initialization geometry as image geometry, the original geometry remains " "unchanged."; cloned->ImageGeometryOn(); } Superclass::SetGeometry(cloned); } } void mitk::Image::Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels, int tDim) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d.Clone(), sDim); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image *image) { Initialize(image->GetPixelType(), *image->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData *vtkimagedata, int channels, int tDim, int sDim, int pDim) { if (vtkimagedata == nullptr) return; m_Dimension = vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = vtkimagedata->GetDimensions()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } if (pDim >= 0) { tmpDimensions[1] = pDim; if (m_Dimension < 2) m_Dimension = 2; } if (sDim >= 0) { tmpDimensions[2] = sDim; if (m_Dimension < 3) m_Dimension = 3; } if (tDim >= 0) { tmpDimensions[3] = tDim; if (m_Dimension < 4) m_Dimension = 4; } mitk::PixelType pixelType(MakePixelType(vtkimagedata)); Initialize(pixelType, m_Dimension, tmpDimensions, channels); const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = spacinglist[1]; if (m_Dimension >= 3) spacing[2] = spacinglist[2]; // access origin of vtkImage Point3D origin; double vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = vtkorigin[1]; if (m_Dimension >= 3) origin[2] = vtkorigin[2]; SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction auto *planeGeometry = static_cast(slicedGeometry->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete[] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if (m_Initialized) return ((s >= 0) && (s < (int)m_Dimensions[2]) && (t >= 0) && (t < (int)m_Dimensions[3]) && (n >= 0) && (n < (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if (m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if (m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if (m_OffsetTable != nullptr) delete[] m_OffsetTable; m_OffsetTable = new size_t[m_Dimension > 4 ? m_Dimension + 1 : 4 + 1]; unsigned int i; size_t num = 1; m_OffsetTable[0] = 1; for (i = 0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i + 1] = num; } for (; i < 4; ++i) m_OffsetTable[i + 1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ((m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0) || (t == 0)); } void mitk::Image::Expand(unsigned int timeSteps) { if (timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; return ((size_t)s) + ((size_t)t) * m_Dimensions[2] + ((size_t)n) * m_Dimensions[3] * m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if (IsValidVolume(t, n) == false) return false; return ((size_t)t) + ((size_t)n) * m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetSliceIndex(s, t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if (vol.GetPointer() != nullptr) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t, n)] = vol = AllocateVolumeData_unlocked(t, n, nullptr, importMemoryManagement); sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; ////ALTERNATIVE: //// allocate new slice // sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); // m_Slices[pos]=sl; // return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetVolumeIndex(t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); return m_Volumes[pos] = vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if (importMemoryManagement == CopyMemory) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); if (data != nullptr) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } else { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos] = vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { ImageDataItemPointer ch; // allocate new channel if (importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); if (data != nullptr) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); } else { ch = new ImageDataItem(this->m_ImageDescriptor, -1, data, importMemoryManagement == ManageMemory); } m_Channels[n] = ch; return ch; } unsigned int *mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete[] m_Dimensions; m_Dimensions = nullptr; } void mitk::Image::SetGeometry(BaseGeometry *aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if (aGeometry3D->GetImageGeometry() == false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is " "pixel-center-based! If it is not, you need to call " "Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream &os, itk::Indent indent) const { if (m_Initialized) { unsigned char i; os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for (i = 0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for (unsigned int ch = 0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os, indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry *geo = this->GetGeometry(); bool ret = false; if (geo) { const vnl_matrix_fixed &mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType ref = 0; for (short k = 0; k < 3; ++k) ref += mx[k][k]; ref /= 1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for (short i = 0; i < 3; ++i) { for (short j = 0; j < 3; ++j) { if (i != j) { if (std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } bool mitk::Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if (rightHandSide.GetDimension() != leftHandSide.GetDimension()) { if (verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide.GetDimension(), leftHandSide.GetDimension()); for (unsigned int i = 0; i < minDimensionality; ++i) { if (rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i)) { returnValue = false; if (verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "leftHandSide->GetDimension(" << i << ") is " << leftHandSide.GetDimension(i) << "rightHandSide->GetDimension(" << i << ") is " << rightHandSide.GetDimension(i); } } } // Pixeltype mitk::PixelType pixelTypeRightHandSide = rightHandSide.GetPixelType(); mitk::PixelType pixelTypeLeftHandSide = leftHandSide.GetPixelType(); if (!(pixelTypeRightHandSide == pixelTypeLeftHandSide)) { if (verbose) { MITK_INFO << "[( Image )] PixelType differs."; MITK_INFO << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString() << "rightHandSide is " << pixelTypeRightHandSide.GetTypeAsString(); } returnValue = false; } // Geometries if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) { MITK_INFO << "[( Image )] Geometries differ."; } returnValue = false; } // Pixel values - default mode [ 0 threshold in difference ] // compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception if (returnValue) { mitk::CompareImageDataFilter::Pointer compareFilter = mitk::CompareImageDataFilter::New(); compareFilter->SetInput(0, &rightHandSide); compareFilter->SetInput(1, &leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if ((!compareFilter->GetResult())) { returnValue = false; if (verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp b/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp index 77e58bf44c..239f195d07 100644 --- a/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp +++ b/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp @@ -1,184 +1,184 @@ /*============================================================================ 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 "mitkImageAccessorBase.h" #include "mitkImage.h" mitk::ImageAccessorBase::ThreadIDType mitk::ImageAccessorBase::CurrentThreadHandle() { #ifdef ITK_USE_SPROC return GetCurrentThreadId(); #endif #ifdef ITK_USE_PTHREADS return pthread_self(); #endif #ifdef ITK_USE_WIN32_THREADS return GetCurrentThreadId(); #endif } bool mitk::ImageAccessorBase::CompareThreadHandles(mitk::ImageAccessorBase::ThreadIDType handle1, mitk::ImageAccessorBase::ThreadIDType handle2) { return handle1 == handle2; } mitk::ImageAccessorBase::~ImageAccessorBase() { } mitk::ImageAccessorBase::ImageAccessorBase(ImageConstPointer image, const ImageDataItem *imageDataItem, int OptionFlags) : // m_Image(iP) //, imageDataItem(iDI) m_SubRegion(nullptr), m_Options(OptionFlags), m_CoherentMemory(false) { m_Thread = CurrentThreadHandle(); // Initialize WaitLock m_WaitLock = new ImageAccessorWaitLock(); m_WaitLock->m_WaiterCount = 0; // Check validity of ImageAccessor // Is there an Image? /* if(!m_Image) { mitkThrow() << "Invalid ImageAccessor: No Image was specified in constructor of ImageAccessor"; } */ if (image) { // Make sure, that the Image is initialized properly if (image->m_Initialized == false) { if (image->GetSource().IsNull()) { mitkThrow() << "ImageAccessor: No image source is defined"; } - image->m_ReadWriteLock.Lock(); + image->m_ReadWriteLock.lock(); if (image->GetSource()->Updating() == false) { image->GetSource()->UpdateOutputInformation(); } - image->m_ReadWriteLock.Unlock(); + image->m_ReadWriteLock.unlock(); } } // Investigate 4 cases of possible image parts/regions // Case 1: No ImageDataItem and no Subregion => Whole Image is accessed if (imageDataItem == nullptr && m_SubRegion == nullptr) { m_CoherentMemory = true; // Organize first image channel - image->m_ReadWriteLock.Lock(); + image->m_ReadWriteLock.lock(); imageDataItem = image->GetChannelData(); - image->m_ReadWriteLock.Unlock(); + image->m_ReadWriteLock.unlock(); // Set memory area m_AddressBegin = imageDataItem->m_Data; m_AddressEnd = (unsigned char *)m_AddressBegin + imageDataItem->m_Size; } // Case 2: ImageDataItem but no Subregion if (imageDataItem && m_SubRegion == nullptr) { m_CoherentMemory = true; // Set memory area m_AddressBegin = imageDataItem->m_Data; m_AddressEnd = (unsigned char *)m_AddressBegin + imageDataItem->m_Size; } // Case 3: No ImageDataItem but a SubRegion if (imageDataItem == nullptr && m_SubRegion) { mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet)."; } // Case 4: ImageDataItem and SubRegion if (imageDataItem == nullptr && m_SubRegion) { mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet)."; } } /** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor object * \throws mitk::Exception if memory area is incoherent (not supported yet) */ bool mitk::ImageAccessorBase::Overlap(const ImageAccessorBase *iAB) { if (m_CoherentMemory) { if ((iAB->m_AddressBegin >= m_AddressBegin && iAB->m_AddressBegin < m_AddressEnd) || (iAB->m_AddressEnd > m_AddressBegin && iAB->m_AddressEnd <= m_AddressEnd)) { return true; } if ((m_AddressBegin >= iAB->m_AddressBegin && m_AddressBegin < iAB->m_AddressEnd) || (m_AddressEnd > iAB->m_AddressBegin && m_AddressEnd <= iAB->m_AddressEnd)) { return true; } } else { - GetImage()->m_ReadWriteLock.Unlock(); + GetImage()->m_ReadWriteLock.unlock(); mitkThrow() << "ImageAccessor: incoherent memory area is not supported yet"; } return false; } /** \brief Uses the WaitLock to wait for another ImageAccessor*/ void mitk::ImageAccessorBase::WaitForReleaseOf(ImageAccessorWaitLock *wL) { - wL->m_Mutex.Lock(); + wL->m_Mutex.lock(); // Decrement wL->m_WaiterCount -= 1; // If there are no more waiting ImageAccessors, delete the Mutex // (Der Letzte macht das Licht aus!) if (wL->m_WaiterCount <= 0) { - wL->m_Mutex.Unlock(); + wL->m_Mutex.unlock(); delete wL; } else { - wL->m_Mutex.Unlock(); + wL->m_Mutex.unlock(); } } void mitk::ImageAccessorBase::PreventRecursiveMutexLock(mitk::ImageAccessorBase *iAB) { #ifdef MITK_USE_RECURSIVE_MUTEX_PREVENTION // Prevent deadlock ThreadIDType id = CurrentThreadHandle(); if (CompareThreadHandles(id, iAB->m_Thread)) { - GetImage()->m_ReadWriteLock.Unlock(); + GetImage()->m_ReadWriteLock.unlock(); mitkThrow() << "Prohibited image access: the requested image part is already in use and cannot be requested recursively!"; } #endif } diff --git a/Modules/Core/src/DataManagement/mitkImageDataItem.cpp b/Modules/Core/src/DataManagement/mitkImageDataItem.cpp index cc252716cb..407ea0a0ee 100644 --- a/Modules/Core/src/DataManagement/mitkImageDataItem.cpp +++ b/Modules/Core/src/DataManagement/mitkImageDataItem.cpp @@ -1,334 +1,334 @@ /*============================================================================ 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 "mitkImageDataItem.h" #include "mitkMemoryUtilities.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::ImageDataItem::ImageDataItem(const ImageDataItem &aParent, const mitk::ImageDescriptor::Pointer desc, int timestep, unsigned int dimension, void *data, bool manageMemory, size_t offset) : m_Data(static_cast(aParent.m_Data) + offset), m_PixelType(new mitk::PixelType(aParent.GetPixelType())), m_ManageMemory(false), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(offset), m_IsComplete(false), m_Size(0), m_Parent(&aParent), m_Dimension(dimension), m_Timestep(timestep) { // compute size // const unsigned int *dims = desc->GetDimensions(); for (unsigned int i = 0; i < dimension; i++) { m_Dimensions[i] = desc->GetDimensions()[i]; } this->ComputeItemSize(m_Dimensions, dimension); if (data != nullptr && data != m_Data) { memcpy(m_Data, data, m_Size); if (manageMemory) { delete[](unsigned char *) data; } } m_ReferenceCount = 0; } mitk::ImageDataItem::~ImageDataItem() { if (m_VtkImageData != nullptr) { m_VtkImageData->Delete(); } if (m_VtkImageReadAccessor != nullptr) { delete m_VtkImageReadAccessor; } if (m_VtkImageWriteAccessor != nullptr) { delete m_VtkImageWriteAccessor; } if (m_Parent.IsNull()) { if (m_ManageMemory) delete[] m_Data; } delete m_PixelType; } mitk::ImageDataItem::ImageDataItem(const mitk::ImageDescriptor::Pointer desc, int timestep, void *data, bool manageMemory) : m_Data(static_cast(data)), m_PixelType(new mitk::PixelType(desc->GetChannelDescriptor(0).GetPixelType())), m_ManageMemory(manageMemory), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(0), m_IsComplete(false), m_Size(0), m_Dimension(desc->GetNumberOfDimensions()), m_Timestep(timestep) { // compute size const unsigned int *dimensions = desc->GetDimensions(); for (unsigned int i = 0; i < m_Dimension; i++) { m_Dimensions[i] = dimensions[i]; } this->ComputeItemSize(m_Dimensions, m_Dimension); if (m_Data == nullptr) { m_Data = mitk::MemoryUtilities::AllocateElements(m_Size); m_ManageMemory = true; } m_ReferenceCount = 0; } mitk::ImageDataItem::ImageDataItem(const mitk::PixelType &type, int timestep, unsigned int dimension, unsigned int *dimensions, void *data, bool manageMemory) : m_Data(static_cast(data)), m_PixelType(new mitk::PixelType(type)), m_ManageMemory(manageMemory), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(0), m_IsComplete(false), m_Size(0), m_Parent(nullptr), m_Dimension(dimension), m_Timestep(timestep) { for (unsigned int i = 0; i < m_Dimension; i++) { m_Dimensions[i] = dimensions[i]; } this->ComputeItemSize(dimensions, dimension); if (m_Data == nullptr) { m_Data = mitk::MemoryUtilities::AllocateElements(m_Size); m_ManageMemory = true; } m_ReferenceCount = 0; } mitk::ImageDataItem::ImageDataItem(const ImageDataItem &other) : itk::LightObject(), m_Data(other.m_Data), m_PixelType(new mitk::PixelType(*other.m_PixelType)), m_ManageMemory(other.m_ManageMemory), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(other.m_Offset), m_IsComplete(other.m_IsComplete), m_Size(other.m_Size), m_Parent(other.m_Parent), m_Dimension(other.m_Dimension), m_Timestep(other.m_Timestep) { // copy m_Data ?? for (int i = 0; i < MAX_IMAGE_DIMENSIONS; ++i) m_Dimensions[i] = other.m_Dimensions[i]; } itk::LightObject::Pointer mitk::ImageDataItem::InternalClone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::ImageDataItem::ComputeItemSize(const unsigned int *dimensions, unsigned int dimension) { m_Size = m_PixelType->GetSize(); for (unsigned int i = 0; i < dimension; i++) { m_Size *= *(dimensions + i); } } void mitk::ImageDataItem::ConstructVtkImageData(ImageConstPointer iP) const { vtkImageData *inData = vtkImageData::New(); vtkDataArray *scalars = nullptr; const unsigned int *dims = m_Dimensions; const unsigned int dim = m_Dimension; unsigned long size = 0; if (dim == 1) { inData->SetDimensions(dims[0] - 1, 1, 1); size = dims[0]; inData->SetOrigin(((mitk::ScalarType)dims[0]) / 2.0, 0, 0); } else if (dim == 2) { inData->SetDimensions(dims[0], dims[1], 1); size = dims[0] * dims[1]; inData->SetOrigin(((mitk::ScalarType)dims[0]) / 2.0f, ((mitk::ScalarType)dims[1]) / 2.0f, 0); } else if (dim >= 3) { inData->SetDimensions(dims[0], dims[1], dims[2]); size = dims[0] * dims[1] * dims[2]; // Test // inData->SetOrigin( (float) dims[0] / 2.0f, (float) dims[1] / 2.0f, (float) dims[2] / 2.0f ); inData->SetOrigin(0, 0, 0); } else { inData->Delete(); return; } if (m_Timestep >= 0) { SlicedGeometry3D *geom3d; geom3d = iP->GetSlicedGeometry(m_Timestep); const mitk::Vector3D vspacing = geom3d->GetSpacing(); double dspacing[3] = {vspacing[0], vspacing[1], vspacing[2]}; inData->SetSpacing(dspacing); } - if (m_PixelType->GetComponentType() == itk::ImageIOBase::CHAR) + if (m_PixelType->GetComponentType() == itk::IOComponentEnum::CHAR) { scalars = vtkCharArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::UCHAR) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::UCHAR) { scalars = vtkUnsignedCharArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::SHORT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::SHORT) { scalars = vtkShortArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::USHORT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::USHORT) { scalars = vtkUnsignedShortArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::INT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::INT) { scalars = vtkIntArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::UINT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::UINT) { scalars = vtkUnsignedIntArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::LONG) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::LONG) { scalars = vtkLongArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::ULONG) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::ULONG) { scalars = vtkUnsignedLongArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::FLOAT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::FLOAT) { scalars = vtkFloatArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::DOUBLE) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::DOUBLE) { scalars = vtkDoubleArray::New(); } else { inData->Delete(); return; } m_VtkImageData = inData; // set mitk imageDataItem void array to vtk scalar values scalars->SetNumberOfComponents(m_PixelType->GetNumberOfComponents()); scalars->SetVoidArray(m_Data, size * m_PixelType->GetNumberOfComponents(), 1); m_VtkImageData->GetPointData()->SetScalars(scalars); scalars->Delete(); } void mitk::ImageDataItem::Modified() const { if (m_VtkImageData) m_VtkImageData->Modified(); } mitk::ImageVtkReadAccessor *mitk::ImageDataItem::GetVtkImageAccessor(mitk::ImageDataItem::ImageConstPointer iP) const { if (m_VtkImageData == nullptr) { ConstructVtkImageData(iP); } if (m_VtkImageReadAccessor == nullptr) { m_VtkImageReadAccessor = new ImageVtkReadAccessor(iP, this, m_VtkImageData); } return m_VtkImageReadAccessor; } mitk::ImageVtkWriteAccessor *mitk::ImageDataItem::GetVtkImageAccessor(ImagePointer iP) { if (m_VtkImageData == nullptr) { ConstructVtkImageData(iP.GetPointer()); } if (m_VtkImageWriteAccessor == nullptr) { m_VtkImageWriteAccessor = new ImageVtkWriteAccessor(iP, this, m_VtkImageData); } return m_VtkImageWriteAccessor; } diff --git a/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp index 328e0869ee..a1a62914a6 100644 --- a/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp @@ -1,147 +1,147 @@ /*============================================================================ 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 "mitkImageReadAccessor.h" #include "mitkImage.h" mitk::ImageReadAccessor::ImageReadAccessor(ImageConstPointer image, const mitk::ImageDataItem *iDI, int OptionFlags) : ImageAccessorBase(image, iDI, OptionFlags), m_Image(image) { if (!(OptionFlags & ImageAccessorBase::IgnoreLock)) { try { OrganizeReadAccess(); } catch (...) { delete m_WaitLock; throw; } } } mitk::ImageReadAccessor::ImageReadAccessor(ImagePointer image, const mitk::ImageDataItem *iDI, int OptionFlags) : ImageAccessorBase(image.GetPointer(), iDI, OptionFlags), m_Image(image.GetPointer()) { if (!(OptionFlags & ImageAccessorBase::IgnoreLock)) { try { OrganizeReadAccess(); } catch (...) { delete m_WaitLock; throw; } } } mitk::ImageReadAccessor::ImageReadAccessor(const mitk::Image *image, const ImageDataItem *iDI) : ImageAccessorBase(image, iDI, ImageAccessorBase::DefaultBehavior), m_Image(image) { OrganizeReadAccess(); } mitk::ImageReadAccessor::~ImageReadAccessor() { if (!(m_Options & ImageAccessorBase::IgnoreLock)) { // Future work: In case of non-coherent memory, copied area needs to be deleted - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); // delete self from list of ImageReadAccessors in Image auto it = std::find(m_Image->m_Readers.begin(), m_Image->m_Readers.end(), this); m_Image->m_Readers.erase(it); // delete lock, if there are no waiting ImageAccessors if (m_WaitLock->m_WaiterCount <= 0) { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); delete m_WaitLock; } else { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); } - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } else { delete m_WaitLock; } } const mitk::Image *mitk::ImageReadAccessor::GetImage() const { return m_Image.GetPointer(); } void mitk::ImageReadAccessor::OrganizeReadAccess() { - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); // Check, if there is any Write-Access going on if (m_Image->m_Writers.size() > 0) { // Check for every WriteAccessors, if the Region of this ImageAccessors overlaps // make sure this iterator is not used, when m_ReadWriteLock is Unlocked! auto it = m_Image->m_Writers.begin(); for (; it != m_Image->m_Writers.end(); ++it) { ImageAccessorBase *w = *it; if (Overlap(w)) { // An Overlap was detected. There are two possibilities to deal with this situation: // Throw an exception or wait for the WriteAccessor w until it is released and start again with the request // afterwards. if (!(m_Options & ExceptionIfLocked)) { PreventRecursiveMutexLock(w); // WAIT w->Increment(); - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); ImageAccessorBase::WaitForReleaseOf(w->m_WaitLock); // after waiting for the WriteAccessor w, start this method again OrganizeReadAccess(); return; } else { // THROW EXCEPTION - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); mitkThrowException(mitk::MemoryIsLockedException) << "The image part being ordered by the ImageAccessor is already in use and locked"; return; } } // if } // for } // if // Now, we know, that there is no conflict with a Write-Access // Lock the Mutex in ImageAccessorBase, to make sure that every other ImageAccessor has to wait if it locks the mutex - m_WaitLock->m_Mutex.Lock(); + m_WaitLock->m_Mutex.lock(); // insert self into readers list in Image m_Image->m_Readers.push_back(this); // printf("ReadAccess %d %d\n",(int) m_Image->m_Readers.size(),(int) m_Image->m_Writers.size()); // fflush(0); - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } diff --git a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp index 181c995da1..811f2029c8 100644 --- a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp +++ b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp @@ -1,348 +1,348 @@ /*============================================================================ 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 "mitkImageStatisticsHolder.h" #include "mitkHistogramGenerator.h" #include #include "mitkImageAccessByItk.h" //#define BOUNDINGOBJECT_IGNORE mitk::ImageStatisticsHolder::ImageStatisticsHolder(mitk::Image *image) : m_Image(image) { m_CountOfMinValuedVoxels.resize(1, 0); m_CountOfMaxValuedVoxels.resize(1, 0); m_ScalarMin.resize(1, itk::NumericTraits::max()); m_ScalarMax.resize(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(1, itk::NumericTraits::max()); m_Scalar2ndMax.resize(1, itk::NumericTraits::NonpositiveMin()); mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New(); m_HistogramGeneratorObject = generator; } mitk::ImageStatisticsHolder::~ImageStatisticsHolder() { m_HistogramGeneratorObject = nullptr; } const mitk::ImageStatisticsHolder::HistogramType *mitk::ImageStatisticsHolder::GetScalarHistogram( int t, unsigned int /*component*/) { mitk::ImageTimeSelector *timeSelector = this->GetTimeSelector(); if (timeSelector != nullptr) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); auto *generator = static_cast(m_HistogramGeneratorObject.GetPointer()); generator->SetImage(timeSelector->GetOutput()); generator->ComputeHistogram(); return static_cast(generator->GetHistogram()); } return nullptr; } bool mitk::ImageStatisticsHolder::IsValidTimeStep(int t) const { return m_Image->IsValidTimeStep(t); } mitk::ImageTimeSelector::Pointer mitk::ImageStatisticsHolder::GetTimeSelector() { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Image); return timeSelector; } void mitk::ImageStatisticsHolder::Expand(unsigned int timeSteps) { if (!m_Image->IsValidTimeStep(timeSteps - 1)) return; // The BaseData needs to be expanded, call the mitk::Image::Expand() method m_Image->Expand(timeSteps); if (timeSteps > m_ScalarMin.size()) { m_ScalarMin.resize(timeSteps, itk::NumericTraits::max()); m_ScalarMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(timeSteps, itk::NumericTraits::max()); m_Scalar2ndMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.resize(timeSteps, 0); m_CountOfMaxValuedVoxels.resize(timeSteps, 0); } } void mitk::ImageStatisticsHolder::ResetImageStatistics() { m_ScalarMin.assign(1, itk::NumericTraits::max()); m_ScalarMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.assign(1, itk::NumericTraits::max()); m_Scalar2ndMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.assign(1, 0); m_CountOfMaxValuedVoxels.assign(1, 0); } /// \cond SKIP_DOXYGEN template void mitk::_ComputeExtremaInItkImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); typedef typename ItkImageType::PixelType TPixel; TPixel value = 0; if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { value = it.Get(); #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); } /// \endcond SKIP_DOXYGEN /// \cond SKIP_DOXYGEN template void mitk::_ComputeExtremaInItkVectorImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t, unsigned int component) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { double value = it.Get()[component]; #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); } /// \endcond SKIP_DOXYGEN void mitk::ImageStatisticsHolder::ComputeImageStatistics(int t, unsigned int component) { // timestep valid? if (!m_Image->IsValidTimeStep(t)) return; // image modified? if (this->m_Image->GetMTime() > m_LastRecomputeTimeStamp.GetMTime()) this->ResetImageStatistics(); Expand(t + 1); // do we have valid information already? if (m_ScalarMin[t] != itk::NumericTraits::max() || m_Scalar2ndMin[t] != itk::NumericTraits::max()) return; // Values already calculated before... // used to avoid statistics calculation on Odf images. property will be replaced as soons as bug 17928 is merged and // the diffusion image refactoring is complete. mitk::BoolProperty *isSh = dynamic_cast(m_Image->GetProperty("IsShImage").GetPointer()); mitk::BoolProperty *isOdf = dynamic_cast(m_Image->GetProperty("IsOdfImage").GetPointer()); const mitk::PixelType pType = m_Image->GetPixelType(0); - if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::ImageIOBase::UNKNOWNPIXELTYPE) && - (pType.GetPixelType() != itk::ImageIOBase::VECTOR)) + if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::IOPixelEnum::UNKNOWNPIXELTYPE) && + (pType.GetPixelType() != itk::IOPixelEnum::VECTOR)) { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessByItk_2(image, _ComputeExtremaInItkImage, this, t); } } - else if (pType.GetPixelType() == itk::ImageIOBase::VECTOR && + else if (pType.GetPixelType() == itk::IOPixelEnum::VECTOR && (!isOdf || !isOdf->GetValue()) && (!isSh || !isSh->GetValue())) // we have a vector image { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessVectorPixelTypeByItk_n(image, _ComputeExtremaInItkVectorImage, (this, t, component)); } } else { m_ScalarMin[t] = 0; m_ScalarMax[t] = 255; m_Scalar2ndMin[t] = 0; m_Scalar2ndMax[t] = 255; } } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMinValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMinValuedVoxels[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMaxValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMaxValuedVoxels[t]; } diff --git a/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp index acc2cdbd24..49335d337b 100644 --- a/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp @@ -1,54 +1,54 @@ /*============================================================================ 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 "mitkImageVtkReadAccessor.h" #include "mitkImage.h" #include const mitk::Image *mitk::ImageVtkReadAccessor::GetImage() const { return m_Image; } mitk::ImageVtkReadAccessor::ImageVtkReadAccessor(ImageConstPointer iP, const mitk::ImageDataItem *iDI, const vtkImageData *imageDataVtk) : ImageAccessorBase(iP, iDI), m_Image(iP.GetPointer()), m_ImageDataVtk(imageDataVtk) { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); m_Image->m_VtkReaders.push_back(this); // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } mitk::ImageVtkReadAccessor::~ImageVtkReadAccessor() { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); auto it = std::find(m_Image->m_VtkReaders.begin(), m_Image->m_VtkReaders.end(), this); if (it != m_Image->m_VtkReaders.end()) { m_Image->m_VtkReaders.erase(it); } // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } const vtkImageData *mitk::ImageVtkReadAccessor::GetVtkImageData() const { return m_ImageDataVtk; } diff --git a/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp index 8d860f03e1..15fc124e00 100644 --- a/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp @@ -1,54 +1,54 @@ /*============================================================================ 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 "mitkImageVtkWriteAccessor.h" #include "mitkImage.h" #include const mitk::Image *mitk::ImageVtkWriteAccessor::GetImage() const { return m_Image; } mitk::ImageVtkWriteAccessor::ImageVtkWriteAccessor(ImagePointer iP, const mitk::ImageDataItem *iDI, vtkImageData *imageDataVtk) : ImageAccessorBase(nullptr, iDI), m_Image(iP.GetPointer()), m_ImageDataVtk(imageDataVtk) { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); m_Image->m_VtkReaders.push_back(this); // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } mitk::ImageVtkWriteAccessor::~ImageVtkWriteAccessor() { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); auto it = std::find(m_Image->m_VtkReaders.begin(), m_Image->m_VtkReaders.end(), this); if (it != m_Image->m_VtkReaders.end()) { m_Image->m_VtkReaders.erase(it); } // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } vtkImageData *mitk::ImageVtkWriteAccessor::GetVtkImageData() const { return m_ImageDataVtk; } diff --git a/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp index c03cbe3db3..ea906399bd 100644 --- a/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp @@ -1,147 +1,147 @@ /*============================================================================ 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 "mitkImageWriteAccessor.h" mitk::ImageWriteAccessor::ImageWriteAccessor(ImagePointer image, const mitk::ImageDataItem *iDI, int OptionFlags) : ImageAccessorBase(image.GetPointer(), iDI, OptionFlags), m_Image(image) { OrganizeWriteAccess(); } mitk::ImageWriteAccessor::~ImageWriteAccessor() { // In case of non-coherent memory, copied area needs to be written back // TODO - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); // delete self from list of ImageReadAccessors in Image auto it = std::find(m_Image->m_Writers.begin(), m_Image->m_Writers.end(), this); m_Image->m_Writers.erase(it); // delete lock, if there are no waiting ImageAccessors if (m_WaitLock->m_WaiterCount <= 0) { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); delete m_WaitLock; } else { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); } - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } const mitk::Image *mitk::ImageWriteAccessor::GetImage() const { return m_Image.GetPointer(); } void mitk::ImageWriteAccessor::OrganizeWriteAccess() { - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); bool readOverlap = false; bool writeOverlap = false; ImageAccessorWaitLock *overlapLock = nullptr; // Check, if there is any Read-Access going on if (m_Image->m_Readers.size() > 0) { // Check for every ReadAccessor, if the Region of this ImageAccessors overlaps // make sure this iterator is not used, when m_ReadWriteLock is Unlocked! auto it = m_Image->m_Readers.begin(); for (; it != m_Image->m_Readers.end(); ++it) { ImageAccessorBase *r = *it; if ((r->m_Options & IgnoreLock) == 0 && Overlap(r)) { // An Overlap was detected. PreventRecursiveMutexLock(r); readOverlap = true; overlapLock = r->m_WaitLock; break; } // if } // for } // if // Check, if there is any Write-Access going on if (m_Image->m_Writers.size() > 0) { // Check for every WriteAccessor, if the Region of this ImageAccessors overlaps // make sure this iterator is not used, when m_ReadWriteLock is Unlocked! auto it = m_Image->m_Writers.begin(); for (; it != m_Image->m_Writers.end(); ++it) { ImageAccessorBase *w = *it; if ((w->m_Options & IgnoreLock) == 0 && Overlap(w)) { // An Overlap was detected. PreventRecursiveMutexLock(w); // save overlapping Waitlock writeOverlap = true; overlapLock = w->m_WaitLock; break; } // if } // for } // if if (readOverlap || writeOverlap) { // Throw an exception or wait for the WriteAccessor w until it is released and start again with the request // afterwards. if (!(m_Options & ExceptionIfLocked)) { // WAIT overlapLock->m_WaiterCount += 1; - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); ImageAccessorBase::WaitForReleaseOf(overlapLock); // after waiting for the ImageAccessor, start this method again OrganizeWriteAccess(); return; } else { // THROW EXCEPTION - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); mitkThrowException(mitk::MemoryIsLockedException) << "The image part being ordered by the ImageAccessor is already in use and locked"; // MITK_ERROR("Speicherbereich belegt"); return; } } // Now, we know, that there is no conflict with a Read- or Write-Access // Lock the Mutex in ImageAccessorBase, to make sure that every other ImageAccessor has to wait - m_WaitLock->m_Mutex.Lock(); + m_WaitLock->m_Mutex.lock(); // insert self into Writers list in Image m_Image->m_Writers.push_back(this); // printf("WriteAccess %d %d\n",(int) m_Image->m_Readers.size(),(int) m_Image->m_Writers.size()); // fflush(0); - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index 6da6ebc0b1..6ef243cb93 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -1,507 +1,507 @@ /*============================================================================ 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 "mitkLevelWindow.h" #include "mitkImage.h" #include "mitkImageSliceSelector.h" #include "mitkImageStatisticsHolder.h" #include void mitk::LevelWindow::EnsureConsistency() { // Check if total range is ok { if (m_RangeMin > m_RangeMax) std::swap(m_RangeMin, m_RangeMax); if (m_RangeMin == m_RangeMax) m_RangeMin = m_RangeMax - 1; } // Check if current window is ok { if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound <= m_RangeMin) m_LowerWindowBound = m_RangeMin; if (m_UpperWindowBound <= m_RangeMin) m_UpperWindowBound = m_RangeMin + 1; if (m_LowerWindowBound >= m_RangeMax) m_LowerWindowBound = m_RangeMax - 1; if (m_UpperWindowBound >= m_RangeMax) m_UpperWindowBound = m_RangeMax; if (m_LowerWindowBound == m_UpperWindowBound) { m_UpperWindowBound += 0.5; m_LowerWindowBound -= 0.5; m_UpperWindowBound = std::min(m_UpperWindowBound, m_RangeMax); m_LowerWindowBound = std::max(m_LowerWindowBound, m_RangeMin); } } } mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window) : m_LowerWindowBound(level - window / 2.0), m_UpperWindowBound(level + window / 2.0), m_RangeMin(-2048.0), m_RangeMax(4096.0), m_DefaultLowerBound(-2048.0), m_DefaultUpperBound(4096.0), m_IsFloatingImage(false), m_Fixed(false) { SetDefaultLevelWindow(level, window); SetLevelWindow(level, window, true); } mitk::LevelWindow::LevelWindow(const mitk::LevelWindow &levWin) : m_LowerWindowBound(levWin.GetLowerWindowBound()), m_UpperWindowBound(levWin.GetUpperWindowBound()), m_RangeMin(levWin.GetRangeMin()), m_RangeMax(levWin.GetRangeMax()), m_DefaultLowerBound(levWin.GetDefaultLowerBound()), m_DefaultUpperBound(levWin.GetDefaultUpperBound()), m_IsFloatingImage(levWin.IsFloatingValues()), m_Fixed(levWin.GetFixed()) { } mitk::LevelWindow::~LevelWindow() { } mitk::ScalarType mitk::LevelWindow::GetLevel() const { return (m_UpperWindowBound - m_LowerWindowBound) / 2.0 + m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetWindow() const { return (m_UpperWindowBound - m_LowerWindowBound); } mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const { return ((m_DefaultUpperBound + m_DefaultLowerBound) / 2.0); } mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const { return ((m_DefaultUpperBound - m_DefaultLowerBound)); } void mitk::LevelWindow::ResetDefaultLevelWindow() { SetLevelWindow(GetDefaultLevel(), GetDefaultWindow()); } mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const { return m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const { return m_UpperWindowBound; } void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window) { SetDefaultBoundaries((level - (window / 2.0)), (level + (window / 2.0))); } void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary) { SetWindowBounds((level - (window / 2.0)), (level + (window / 2.0)), expandRangesIfNecessary); } void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound, mitk::ScalarType upperBound, bool expandRangesIfNecessary) { if (IsFixed()) return; m_LowerWindowBound = lowerBound; m_UpperWindowBound = upperBound; if (expandRangesIfNecessary) { /* if caller is sure he wants exactly that level/window, we make sure the limits match */ if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound < m_RangeMin) { m_RangeMin = m_LowerWindowBound; } if (m_UpperWindowBound > m_RangeMax) { m_RangeMax = m_UpperWindowBound; } } EnsureConsistency(); } void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max) { if (IsFixed()) return; m_RangeMin = min; m_RangeMax = max; EnsureConsistency(); } void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up) { if (IsFixed()) return; m_DefaultLowerBound = low; m_DefaultUpperBound = up; // Check if default window is ok { if (m_DefaultLowerBound > m_DefaultUpperBound) std::swap(m_DefaultLowerBound, m_DefaultUpperBound); if (m_DefaultLowerBound == m_DefaultUpperBound) m_DefaultLowerBound--; } EnsureConsistency(); } void mitk::LevelWindow::SetToMaxWindowSize() { SetWindowBounds(m_RangeMin, m_RangeMax); } mitk::ScalarType mitk::LevelWindow::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetRangeMax() const { return m_RangeMax; } mitk::ScalarType mitk::LevelWindow::GetRange() const { return m_RangeMax - m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const { return m_DefaultUpperBound; } mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const { return m_DefaultLowerBound; } void mitk::LevelWindow::ResetDefaultRangeMinMax() { SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound); } /*! This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows: Default to taking the central image slice for quick analysis. Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and largest (maxValue) data value by traversing the pixel values only once. In the same scan it also computes the count of minValue values and maxValue values. After that a basic histogram with specific information about the extrems is complete. If minValue == maxValue, the center slice is uniform and the above scan is repeated for the complete image, not just one slice Next, special cases of images with only 1, 2 or 3 distinct data values have hand assigned level window ranges. Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue]) For count(minValue) > 20% the smallest values are frequent and should be distinct from the min2ndValue and larger values (minValue may be std:min, may signify something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR For count(minValue) <= 20% the smallest values are not so important and can blend with the next ones => min(level window) = min2ndValue And analog for max(level window): count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR count(max2ndValue) < 20%: max(level window) = max2ndValue In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range In consequence the level window maximizes contrast with minimal amount of computation and does do useful things if the data contains std::min or std:max values or has only 1 or 2 or 3 data values. */ void mitk::LevelWindow::SetAuto(const mitk::Image *image, bool /*tryPicTags*/, bool guessByCentralSlice, unsigned selectedComponent) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; - if (itk::ImageIOBase::IOComponentType::FLOAT == image->GetPixelType().GetComponentType() - || itk::ImageIOBase::IOComponentType::DOUBLE == image->GetPixelType().GetComponentType()) + if (itk::IOComponentEnum::FLOAT == image->GetPixelType().GetComponentType() + || itk::IOComponentEnum::DOUBLE == image->GetPixelType().GetComponentType()) { m_IsFloatingImage = true; } else { m_IsFloatingImage = false; } const mitk::Image *wholeImage = image; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); if (guessByCentralSlice) { sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); image = sliceSelector->GetOutput(); if (image == nullptr || !image->IsInitialized()) return; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); if (minValue == maxValue) { // guessByCentralSlice seems to have failed, lets look at all data image = wholeImage; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } } else { const_cast(image)->Update(); minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); for (unsigned int i = 1; i < image->GetDimension(3); ++i) { ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i, selectedComponent); if (minValue > minValueTemp) minValue = minValueTemp; ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i); if (maxValue < maxValueTemp) maxValue = maxValueTemp; ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i); if (min2ndValue > min2ndValueTemp) min2ndValue = min2ndValueTemp; ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i); if (max2ndValue > max2ndValueTemp) max2ndValue = max2ndValueTemp; } } // Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt - if (image->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR && - image->GetPixelType().GetComponentType() == itk::ImageIOBase::INT && image->GetPixelType().GetBpe() >= 8) + if (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR && + image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8) { // the windows compiler complains about ambiguos 'pow' call, therefore static casting to (double, int) if (minValue == -(pow((double)2.0, static_cast(image->GetPixelType().GetBpe() / 2)))) { minValue = min2ndValue; } } // End fix //// uniform image if (minValue == maxValue) { minValue = maxValue - 1; } else { // Due to bug #8690 level window now is no longer of fixed range by default but the range adapts according to // levelwindow interaction // This is done because the range should be a little bit larger from the beginning so that the scale doesn't start // to resize right from the beginning double additionalRange = 0.15 * (maxValue - minValue); minValue -= additionalRange; maxValue += additionalRange; } if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); size_t numPixelsInDataset = image->GetDimensions()[0]; for (decltype(image->GetDimension()) k = 1; k < image->GetDimension(); ++k) numPixelsInDataset *= image->GetDimensions()[k]; const auto minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); const auto maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); const auto minCountFraction = minCount / static_cast(numPixelsInDataset); const auto maxCountFraction = maxCount / static_cast(numPixelsInDataset); //// binary image if (min2ndValue == maxValue) { // noop; full range is fine } //// triple value image, put middle value in center of gray level ramp else if (min2ndValue == max2ndValue) { ScalarType minDelta = std::min(min2ndValue - minValue, maxValue - min2ndValue); minValue = min2ndValue - minDelta; maxValue = min2ndValue + minDelta; } // now we can assume more than three distict scalar values else { ScalarType innerRange = max2ndValue - min2ndValue; if (minCountFraction > 0.2) //// lots of min values -> make different from rest, but not miles away { ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange / 2.0; minValue = std::max(minValue, halfInnerRangeGapMinValue); } else //// few min values -> focus on innerRange { minValue = min2ndValue; } if (maxCountFraction > 0.2) //// lots of max values -> make different from rest { ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange / 2.0; maxValue = std::min(maxValue, halfInnerRangeGapMaxValue); } else //// few max values -> focus on innerRange { maxValue = max2ndValue; } } SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetToImageRange(const mitk::Image *image) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; ScalarType minValue = image->GetStatistics()->GetScalarValueMin(0); if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } ScalarType maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetFixed(bool fixed) { m_Fixed = fixed; } bool mitk::LevelWindow::GetFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFloatingValues() const { return m_IsFloatingImage; } void mitk::LevelWindow::SetFloatingValues(bool value) { m_IsFloatingImage = value; } bool mitk::LevelWindow::operator==(const mitk::LevelWindow &levWin) const { return mitk::Equal(this->m_RangeMin, levWin.m_RangeMin, mitk::sqrteps) && mitk::Equal(this->m_RangeMax, levWin.m_RangeMax, mitk::sqrteps) && mitk::Equal(this->m_DefaultLowerBound, levWin.m_DefaultLowerBound, mitk::sqrteps) && mitk::Equal(this->m_DefaultUpperBound, levWin.m_DefaultUpperBound, mitk::sqrteps) && mitk::Equal(this->m_LowerWindowBound, levWin.m_LowerWindowBound, mitk::sqrteps) && mitk::Equal(this->m_UpperWindowBound, levWin.m_UpperWindowBound, mitk::sqrteps) && m_Fixed == levWin.IsFixed() && m_IsFloatingImage == levWin.IsFloatingValues(); } bool mitk::LevelWindow::operator!=(const mitk::LevelWindow &levWin) const { return !((*this) == levWin); } mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin) { if (this == &levWin) { return *this; } else { m_RangeMin = levWin.GetRangeMin(); m_RangeMax = levWin.GetRangeMax(); m_LowerWindowBound = levWin.GetLowerWindowBound(); m_UpperWindowBound = levWin.GetUpperWindowBound(); m_DefaultLowerBound = levWin.GetDefaultLowerBound(); m_DefaultUpperBound = levWin.GetDefaultUpperBound(); m_Fixed = levWin.GetFixed(); m_IsFloatingImage = levWin.IsFloatingValues(); return *this; } } diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp index 4a6565d4e7..48ef1a98b0 100644 --- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp @@ -1,971 +1,971 @@ /*============================================================================ 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 "mitkPlaneGeometry.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkPlaneOperation.h" #include #include #include namespace mitk { PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry(nullptr) { Initialize(); } PlaneGeometry::~PlaneGeometry() {} PlaneGeometry::PlaneGeometry(const PlaneGeometry &other) : Superclass(other), m_ReferenceGeometry(other.m_ReferenceGeometry) { } bool PlaneGeometry::CheckRotationMatrix(mitk::AffineTransform3D *transform, double epsilon) { bool rotation = true; auto matrix = transform->GetMatrix().GetVnlMatrix(); matrix.normalize_columns(); auto det = vnl_determinant(matrix); if (fabs(det-1.0) > epsilon) { MITK_WARN << "Invalid rotation matrix! Determinant != 1 (" << det << ")"; rotation = false; } vnl_matrix_fixed id; id.set_identity(); auto should_be_id = matrix*matrix.transpose(); should_be_id -= id; auto max = should_be_id.absolute_value_max(); if (max > epsilon) { MITK_WARN << "Invalid rotation matrix! R*R^T != ID. Max value: " << max << " (should be 0)"; rotation = false; } return rotation; } void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) { this->CheckRotationMatrix(transform); } void PlaneGeometry::CheckBounds(const BoundingBox::BoundsArrayType &bounds) { // error: unused parameter 'bounds' // this happens in release mode, where the assert macro is defined empty // hence we "use" the parameter: (void)bounds; // currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0] == 0); assert(bounds[2] == 0); // the unit rectangle must be two-dimensional assert(bounds[1] > 0); assert(bounds[3] > 0); } void PlaneGeometry::IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const { pt_mm[0] = GetExtentInMM(0) / GetExtent(0) * pt_units[0]; pt_mm[1] = GetExtentInMM(1) / GetExtent(1) * pt_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const { pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::IndexToWorld(const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use " "PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0]; vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use " "PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex(const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated, bool top) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0, 0) = spacing[0]; vnlmatrix(1, 1) = spacing[1]; vnlmatrix(2, 2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, mitk::ScalarType height, const AffineTransform3D *transform /* = nullptr */, PlaneGeometry::PlaneOrientation planeorientation /* = Axial */, mitk::ScalarType zPosition /* = 0 */, bool frontside /* = true */, bool rotated /* = false */, bool top /* = true */) { Superclass::Initialize(); /// construct standard view. // We define at the moment "frontside" as: axial from above, // coronal from front (nose), saggital from right. // TODO: Double check with medicals doctors or radiologists [ ]. // We define the orientation in patient's view, e.g. LAI is in a axial cut // (parallel to the triangle ear-ear-nose): // first axis: To the left ear of the patient // seecond axis: To the nose of the patient // third axis: To the legs of the patient. // Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior // (AKA caudal/cranial). // We note on all cases in the following switch block r.h. for right handed // or l.h. for left handed to describe the different cases. // However, which system is chosen is defined at the end of the switch block. // CAVE / be careful: the vectors right and bottom are relative to the plane // and do NOT describe e.g. the right side of the patient. Point3D origin; /** Bottom means downwards, DV means Direction Vector. Both relative to the image! */ VnlVector rightDV(3), bottomDV(3); /** Origin of this plane is by default a zero vector and implicitly in the top-left corner: */ origin.Fill(0); /** This is different to all definitions in MITK, except the QT mouse clicks. * But it is like this here and we don't want to change a running system. * Just be aware, that IN THIS FUNCTION we define the origin at the top left (e.g. your screen). */ /** NormalDirection defines which axis (i.e. column index in the transform matrix) * is perpendicular to the plane: */ int normalDirection; switch (planeorientation) // Switch through our limited choice of standard planes: { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: if (frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose. { if (rotated == false) /** Origin in the top-left corner, x=[1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], * origin=[0,0,zpos]: LAI (r.h.) * * 0---rightDV----> | * | | * | Picture of a finite, rectangular plane | * | ( insert LOLCAT-scan here ^_^ ) | * | | * v _________________________________________| * */ { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[w,h,zpos]: RPI (r.h.) { // Caveat emptor: Still using top-left as origin of index coordinate system! FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient. { if (rotated == false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[0,h,zpos]: LPS (r.h.) { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system. break; // Frontal is known as Coronal in mitk. Plane cuts through patient's ear-ear-heel-heel: case Frontal: if (frontside) { if (rotated == false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.) { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.) { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; // Normal vector = posterior direction. break; case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face. if (frontside) { if (rotated == false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.) { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.) { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system. break; default: itkExceptionMacro("unknown PlaneOrientation"); } VnlVector normal(3); FillVector3D(normal, 0, 0, 0); normal[normalDirection] = top ? 1 : -1; if ( transform != nullptr ) { origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ); bottomDV = transform->TransformVector( bottomDV ); normal = transform->TransformVector( normal ); } ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); AffineTransform3D::Pointer planeTransform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, bottomDV); matrix.GetVnlMatrix().set_column(2, normal); planeTransform->SetMatrix(matrix); planeTransform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); this->SetIndexToWorldTransform(planeTransform); this->SetOrigin(origin); } std::vector< int > PlaneGeometry::CalculateDominantAxes(mitk::AffineTransform3D::MatrixType::InternalMatrixType& rotation_matrix) { std::vector< int > axes; bool dominant_axis_error = false; for (int i = 0; i < 3; ++i) { int dominantAxis = itk::Function::Max3( rotation_matrix[0][i], rotation_matrix[1][i], rotation_matrix[2][i] ); for (int j=0; jSetReferenceGeometry(geometry3D); ScalarType width, height; // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetTranspose(); /// The index of the sagittal, coronal and axial axes in the reference geometry. auto axes = CalculateDominantAxes(inverseMatrix); /// The direction of the sagittal, coronal and axial axes in the reference geometry. /// +1 means that the direction is straight, i.e. greater index translates to greater /// world coordinate. -1 means that the direction is inverted. int directions[3]; ScalarType extents[3]; ScalarType spacings[3]; for (int i=0; i<3; ++i) { int dominantAxis = axes.at(i); directions[i] = itk::Function::Sign(inverseMatrix[dominantAxis][i]); extents[i] = geometry3D->GetExtent(dominantAxis); spacings[i] = geometry3D->GetSpacing()[dominantAxis]; } // matrix(column) = inverseTransformMatrix(row) * flippedAxes * spacing matrix[0][0] = inverseMatrix[axes[0]][0] * directions[0] * spacings[0]; matrix[1][0] = inverseMatrix[axes[0]][1] * directions[0] * spacings[0]; matrix[2][0] = inverseMatrix[axes[0]][2] * directions[0] * spacings[0]; matrix[0][1] = inverseMatrix[axes[1]][0] * directions[1] * spacings[1]; matrix[1][1] = inverseMatrix[axes[1]][1] * directions[1] * spacings[1]; matrix[2][1] = inverseMatrix[axes[1]][2] * directions[1] * spacings[1]; matrix[0][2] = inverseMatrix[axes[2]][0] * directions[2] * spacings[2]; matrix[1][2] = inverseMatrix[axes[2]][1] * directions[2] * spacings[2]; matrix[2][2] = inverseMatrix[axes[2]][2] * directions[2] * spacings[2]; /// The "world origin" is the corner with the lowest physical coordinates. /// We use it as a reference point so that we get the correct anatomical /// orientations. Point3D worldOrigin = geometry3D->GetOrigin(); for (int i = 0; i < 3; ++i) { /// The distance of the plane origin from the world origin in voxels. double offset = directions[i] > 0 ? 0.0 : extents[i]; if (geometry3D->GetImageGeometry()) { offset += directions[i] * 0.5; } for (int j = 0; j < 3; ++j) { worldOrigin[j] -= offset * matrix[j][i]; } } switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: width = extents[0]; height = extents[1]; break; case Frontal: width = extents[0]; height = extents[2]; break; case Sagittal: width = extents[1]; height = extents[2]; break; default: itkExceptionMacro("unknown PlaneOrientation"); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(worldOrigin.GetVectorFromOrigin()); InitializeStandardPlane( width, height, transform, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated) { /// The index of the sagittal, coronal and axial axes in world coordinate system. int worldAxis; switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: worldAxis = 2; break; case Frontal: worldAxis = 1; break; case Sagittal: worldAxis = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::ConstPointer affineTransform = geometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix = affineTransform->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); /// The index of the sagittal, coronal and axial axes in the reference geometry. int dominantAxis = CalculateDominantAxes(inverseMatrix).at(worldAxis); ScalarType zPosition = top ? 0.5 : geometry3D->GetExtent(dominantAxis) - 0.5; InitializeStandardPlane(geometry3D, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { ScalarType width = rightVector.two_norm(); ScalarType height = downVector.two_norm(); InitializeStandardPlane(width, height, rightVector, downVector, spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // spacing with 1 or 3 negative components could still make it lefthanded. if (spacing != nullptr) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); this->SetIndexToWorldTransform(transform); } void PlaneGeometry::InitializePlane(const Point3D &origin, const Vector3D &normal) { VnlVector rightVectorVnl(3), downVectorVnl; if (Equal(normal[1], 0.0f) == false) { FillVector3D(rightVectorVnl, 1.0f, -normal[0] / normal[1], 0.0f); rightVectorVnl.normalize(); } else { FillVector3D(rightVectorVnl, 0.0f, 1.0f, 0.0f); } downVectorVnl = vnl_cross_3d(normal.GetVnlVector(), rightVectorVnl); downVectorVnl.normalize(); // Crossproduct vnl_cross_3d is always righthanded. InitializeStandardPlane(rightVectorVnl, downVectorVnl); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness /* = 1.0 */) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // a negative thickness could still make it lefthanded. AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2); } ScalarType PlaneGeometry::DistanceFromPlane(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); } ScalarType PlaneGeometry::SignedDistance(const Point3D &pt3d_mm) const { return SignedDistanceFromPlane(pt3d_mm); } bool PlaneGeometry::IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox) const { if (considerBoundingBox) { Point3D pt3d_units; BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); } else return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct(normal, planeNormal); if (direction.GetSquaredNorm() < eps) return false; crossline.SetDirection(direction); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = (d1 - d2 * N1dN2) / determinant; double c2 = (d2 - d1 * N1dN2) / determinant; Vector3D p = normal * c1 + planeNormal * c2; - crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); + crossline.SetPoint(p); return true; } unsigned int PlaneGeometry::IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const { Line3D crossline; if (this->IntersectionLine(plane, crossline) == false) return 0; Point2D point2; Vector2D direction2; this->Map(crossline.GetPoint(), point2); this->Map(crossline.GetPoint(), crossline.GetDirection(), direction2); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo); } double PlaneGeometry::Angle(const PlaneGeometry *plane) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle(const Line3D &line) const { return vnl_math::pi_over_2 - angle(line.GetDirection().GetVnlVector(), GetMatrixColumn(2)); } bool PlaneGeometry::IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam(const Line3D &line, double &t) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; return true; } bool PlaneGeometry::IsParallel(const PlaneGeometry *plane) const { return ((Angle(plane) < 10.0 * mitk::sqrteps) || (Angle(plane) > (vnl_math::pi - 10.0 * sqrteps))); } bool PlaneGeometry::IsOnPlane(const Point3D &point) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane(const Line3D &line) const { return ((Distance(line.GetPoint()) < eps) && (Distance(line.GetPoint2()) < eps)); } bool PlaneGeometry::IsOnPlane(const PlaneGeometry *plane) const { return (IsParallel(plane) && (Distance(plane->GetOrigin()) < eps)); } Point3D PlaneGeometry::ProjectPointOntoPlane(const Point3D &pt) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane(pt) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation(Operation *operation) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpORIENT: { auto *planeOp = dynamic_cast(operation); if (planeOp == nullptr) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D(defaultVector, 0.0, 0.0, 1.0); Vector3D rotationAxis = itk::CrossProduct(orientationVector, defaultVector); // double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2((double)rotationAxis.GetNorm(), (double)(orientationVector * defaultVector)); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate(center[0], center[1], center[2]); transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]); transform->Translate(-center[0], -center[1], -center[2]); break; } case OpRESTOREPLANEPOSITION: { auto *op = dynamic_cast(operation); if (op == nullptr) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0, 1}; this->SetBounds(bounds); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation(operation); transform->Delete(); return; } this->SetVtkMatrixDeepCopy(transform); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl; os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0); pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1); pt3d_units[2] = 0; return this->GetBoundingBox()->IsInside(pt3d_units); } void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { // pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event) Point3D pt3d_units; pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0)); pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1)); pt3d_units[2] = 0; // pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm // to index units. // pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); // now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as // GetITW->Transform... } void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) { ScalarType bounds[6] = {0, width, 0, height, 0, 1}; ScalarType extent, newextentInMM; if (GetExtent(0) > 0) { extent = GetExtent(0); if (width > extent) newextentInMM = GetExtentInMM(0) / width * extent; else newextentInMM = GetExtentInMM(0) * extent / width; SetExtentInMM(0, newextentInMM); } if (GetExtent(1) > 0) { extent = GetExtent(1); if (width > extent) newextentInMM = GetExtentInMM(1) / height * extent; else newextentInMM = GetExtentInMM(1) * extent / height; SetExtentInMM(1, newextentInMM); } SetBounds(bounds); } bool PlaneGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt3d_units[2] = 0; projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); return this->GetBoundingBox()->IsInside(pt3d_units); } bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); return true; } bool PlaneGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; Superclass::WorldToIndex(atPt3d_mm, pt3d_units); return this->GetBoundingBox()->IsInside(pt3d_units); } bool PlaneGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { Point2D pt2d_mm_start, pt2d_mm_end; Point3D pt3d_mm_end; bool inside = Map(atPt3d_mm, pt2d_mm_start); pt3d_mm_end = atPt3d_mm + vec3d_mm; inside &= Map(pt3d_mm_end, pt2d_mm_end); vec2d_mm = pt2d_mm_end - pt2d_mm_start; return inside; } void PlaneGeometry::Map(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D & /*vec2d_mm*/, mitk::Vector3D & /*vec3d_mm*/) const { //@todo implement parallel to the other Map method! assert(false); } void PlaneGeometry::SetReferenceGeometry(const mitk::BaseGeometry *geometry) { m_ReferenceGeometry = geometry; } const mitk::BaseGeometry *PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; } bool PlaneGeometry::HasReferenceGeometry() const { return (m_ReferenceGeometry != nullptr); } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index d9ad1ac009..994d5af78c 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -1,376 +1,376 @@ /*============================================================================ 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 "mitkPropertyList.h" #include "mitkNumericTypes.h" #include "mitkProperties.h" #include "mitkStringProperty.h" mitk::BaseProperty::ConstPointer mitk::PropertyList::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second.GetPointer(); else return nullptr; }; std::vector mitk::PropertyList::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty() || includeDefaultContext) { for (const auto &property : this->m_Properties) propertyKeys.push_back(property.first); } return propertyKeys; }; std::vector mitk::PropertyList::GetPropertyContextNames() const { return std::vector(); }; mitk::BaseProperty *mitk::PropertyList::GetProperty(const std::string &propertyKey) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second; else return nullptr; } mitk::BaseProperty * mitk::PropertyList::GetNonConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { return this->GetProperty(propertyKey); } void mitk::PropertyList::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!property) return; // make sure that BaseProperty*, which may have just been created and never been // assigned to a SmartPointer, is registered/unregistered properly. If we do not // do that, it will a) not deleted in case it is identical to the old one or // b) possibly deleted when temporarily added to a smartpointer somewhere below. BaseProperty::Pointer tmpSmartPointerToProperty = property; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { // yes // is the property contained in the list identical to the new one? if (it->second->operator==(*property)) { // yes? do nothing and return. return; } if (it->second->AssignProperty(*property)) { // The assignment was successfull this->Modified(); } else { MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first << " of type " << it->second->GetNameOfClass() << " to a property with different type " << property->GetNameOfClass() << "." << " Use ReplaceProperty() instead." << std::endl; } return; } // no? add it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); this->Modified(); } void mitk::PropertyList::ReplaceProperty(const std::string &propertyKey, BaseProperty *property) { if (!property) return; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); } // no? add/replace it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); Modified(); } void mitk::PropertyList::RemoveProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); Modified(); } } mitk::PropertyList::PropertyList() { } mitk::PropertyList::PropertyList(const mitk::PropertyList &other) : itk::Object() { for (auto i = other.m_Properties.cbegin(); i != other.m_Properties.cend(); ++i) { m_Properties.insert(std::make_pair(i->first, i->second->Clone())); } } mitk::PropertyList::~PropertyList() { Clear(); } /** * Consider the list as changed when any of the properties has changed recently. */ -unsigned long mitk::PropertyList::GetMTime() const +itk::ModifiedTimeType mitk::PropertyList::GetMTime() const { for (auto it = m_Properties.cbegin(); it != m_Properties.cend(); ++it) { if (it->second.IsNull()) { itkWarningMacro(<< "Property '" << it->first << "' contains nothing (nullptr)."); continue; } if (Superclass::GetMTime() < it->second->GetMTime()) { Modified(); break; } } return Superclass::GetMTime(); } bool mitk::PropertyList::DeleteProperty(const std::string &propertyKey) { auto it = m_Properties.find(propertyKey); if (it != m_Properties.end()) { it->second = nullptr; m_Properties.erase(it); Modified(); return true; } return false; } void mitk::PropertyList::Clear() { auto it = m_Properties.begin(), end = m_Properties.end(); while (it != end) { it->second = nullptr; ++it; } m_Properties.clear(); } itk::LightObject::Pointer mitk::PropertyList::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace) { if (pList) { const PropertyMap *propertyMap = pList->GetMap(); for (auto iter = propertyMap->cbegin(); // m_PropertyList is created in the constructor, so we don't check it here iter != propertyMap->cend(); ++iter) { const std::string key = iter->first; BaseProperty *value = iter->second; if (replace) { ReplaceProperty(key.c_str(), value); } else { SetProperty(key.c_str(), value); } } } } bool mitk::PropertyList::GetBoolProperty(const char *propertyKey, bool &boolValue) const { BoolProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { boolValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, boolValue); } bool mitk::PropertyList::GetIntProperty(const char *propertyKey, int &intValue) const { IntProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { intValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, intValue); } bool mitk::PropertyList::GetFloatProperty(const char *propertyKey, float &floatValue) const { FloatProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { floatValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, floatValue); } bool mitk::PropertyList::GetStringProperty(const char *propertyKey, std::string &stringValue) const { StringProperty *sp = dynamic_cast(GetProperty(propertyKey)); if (sp != nullptr) { stringValue = sp->GetValue(); return true; } return false; } void mitk::PropertyList::SetIntProperty(const char *propertyKey, int intValue) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::PropertyList::SetBoolProperty(const char *propertyKey, bool boolValue) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::PropertyList::SetFloatProperty(const char *propertyKey, float floatValue) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::PropertyList::SetStringProperty(const char *propertyKey, const char *stringValue) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::PropertyList::Set(const char *propertyKey, bool boolValue) { this->SetBoolProperty(propertyKey, boolValue); } void mitk::PropertyList::Set(const char *propertyKey, int intValue) { this->SetIntProperty(propertyKey, intValue); } void mitk::PropertyList::Set(const char *propertyKey, float floatValue) { this->SetFloatProperty(propertyKey, floatValue); } void mitk::PropertyList::Set(const char *propertyKey, double doubleValue) { this->SetDoubleProperty(propertyKey, doubleValue); } void mitk::PropertyList::Set(const char *propertyKey, const char *stringValue) { this->SetStringProperty(propertyKey, stringValue); } void mitk::PropertyList::Set(const char *propertyKey, const std::string &stringValue) { this->SetStringProperty(propertyKey, stringValue.c_str()); } bool mitk::PropertyList::Get(const char *propertyKey, bool &boolValue) const { return this->GetBoolProperty(propertyKey, boolValue); } bool mitk::PropertyList::Get(const char *propertyKey, int &intValue) const { return this->GetIntProperty(propertyKey, intValue); } bool mitk::PropertyList::Get(const char *propertyKey, float &floatValue) const { return this->GetFloatProperty(propertyKey, floatValue); } bool mitk::PropertyList::Get(const char *propertyKey, double &doubleValue) const { return this->GetDoubleProperty(propertyKey, doubleValue); } bool mitk::PropertyList::Get(const char *propertyKey, std::string &stringValue) const { return this->GetStringProperty(propertyKey, stringValue); } bool mitk::PropertyList::GetDoubleProperty(const char *propertyKey, double &doubleValue) const { DoubleProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { doubleValue = gp->GetValue(); return true; } return false; } void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubleValue) { SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } diff --git a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp index 6b69557d23..4eedfcceeb 100644 --- a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp @@ -1,258 +1,256 @@ /*============================================================================ 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 "mitkStandaloneDataStorage.h" -#include "itkMutexLockHolder.h" -#include "itkSimpleFastMutexLock.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" mitk::StandaloneDataStorage::StandaloneDataStorage() : mitk::DataStorage() { } mitk::StandaloneDataStorage::~StandaloneDataStorage() { for (auto it = m_SourceNodes.begin(); it != m_SourceNodes.end(); ++it) { this->RemoveListeners(it->first); } } bool mitk::StandaloneDataStorage::IsInitialized() const { return true; } void mitk::StandaloneDataStorage::Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents) { { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); /* check if node is in its own list of sources */ if ((parents != nullptr) && (std::find(parents->begin(), parents->end(), node) != parents->end())) throw std::invalid_argument("Node is it's own parent"); /* check if node already exists in StandaloneDataStorage */ if (m_SourceNodes.find(node) != m_SourceNodes.end()) throw std::invalid_argument("Node is already in DataStorage"); /* create parent list if it does not exist */ mitk::DataStorage::SetOfObjects::ConstPointer sp; if (parents != nullptr) sp = parents; else sp = mitk::DataStorage::SetOfObjects::New(); /* Store node and parent list in sources adjacency list */ m_SourceNodes.insert(std::make_pair(node, sp)); /* Store node and an empty children list in derivations adjacency list */ mitk::DataStorage::SetOfObjects::Pointer childrenPointer = mitk::DataStorage::SetOfObjects::New(); mitk::DataStorage::SetOfObjects::ConstPointer children = childrenPointer.GetPointer(); m_DerivedNodes.insert(std::make_pair(node, children)); /* create entry in derivations adjacency list for each parent of the new node */ for (SetOfObjects::ConstIterator it = sp->Begin(); it != sp->End(); it++) { mitk::DataNode::ConstPointer parent = it.Value().GetPointer(); mitk::DataStorage::SetOfObjects::ConstPointer derivedObjects = m_DerivedNodes[parent]; // get or create pointer to list of derived objects for that parent node if (derivedObjects.IsNull()) m_DerivedNodes[parent] = mitk::DataStorage::SetOfObjects::New(); // Create a set of Objects, if it does not already exist auto *deob = const_cast( m_DerivedNodes[parent].GetPointer()); // temporarily get rid of const pointer to insert new element deob->InsertElement(deob->Size(), node); // node is derived from parent. Insert it into the parents list of derived objects } // register for ITK changed events this->AddListeners(node); } /* Notify observers */ EmitAddNodeEvent(node); } void mitk::StandaloneDataStorage::Remove(const mitk::DataNode *node) { if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); if (node == nullptr) return; // remove ITK modified event listener this->RemoveListeners(node); // muellerm, 22.9.10: add additional reference count to ensure // that the node is not deleted when removed from the relation map // while m_Mutex is locked. This would cause the an itk::DeleteEvent // is thrown and a deadlock will occur when event receivers // access the DataStorage again in their event processing function // mitk::DataNode::ConstPointer nodeGuard(node); /* Notify observers of imminent node removal */ EmitRemoveNodeEvent(node); { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); /* remove node from both relation adjacency lists */ this->RemoveFromRelation(node, m_SourceNodes); this->RemoveFromRelation(node, m_DerivedNodes); } } bool mitk::StandaloneDataStorage::Exists(const mitk::DataNode *node) const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); return (m_SourceNodes.find(node) != m_SourceNodes.end()); } void mitk::StandaloneDataStorage::RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation) { for (auto mapIter = relation.cbegin(); mapIter != relation.cend(); ++mapIter) // for each node in the relation if (mapIter->second.IsNotNull()) // if node has a relation list { SetOfObjects::Pointer s = const_cast(mapIter->second.GetPointer()); // search for node to be deleted in the relation list auto relationListIter = std::find( s->begin(), s->end(), node); // this assumes, that the relation list does not contain duplicates (which should be safe to assume) if (relationListIter != s->end()) // if node to be deleted is in relation list s->erase(relationListIter); // remove it from parentlist } /* now remove node from the relation */ AdjacencyList::iterator adIt; adIt = relation.find(node); if (adIt != relation.end()) relation.erase(adIt); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetAll() const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); mitk::DataStorage::SetOfObjects::Pointer resultset = mitk::DataStorage::SetOfObjects::New(); /* Fill resultset with all objects that are managed by the StandaloneDataStorage object */ unsigned int index = 0; for (auto it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it) if (it->first.IsNull()) continue; else resultset->InsertElement(index++, const_cast(it->first.GetPointer())); return SetOfObjects::ConstPointer(resultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetRelations( const mitk::DataNode *node, const AdjacencyList &relation, const NodePredicateBase *condition, bool onlyDirectlyRelated) const { if (node == nullptr) throw std::invalid_argument("invalid node"); /* Either read direct relations directly from adjacency list */ if (onlyDirectlyRelated) { auto it = relation.find(node); // get parents of current node if ((it == relation.cend()) || (it->second.IsNull())) // node not found in list or no set of parents return SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return an empty set else return this->FilterSetOfObjects(it->second, condition); } /* Or traverse adjacency list to collect all related nodes */ std::vector resultset; std::vector openlist; /* Initialize openlist with node. this will add node to resultset, but that is necessary to detect circular relations that would lead to endless recursion */ openlist.push_back(node); while (openlist.size() > 0) { mitk::DataNode::ConstPointer current = openlist.back(); // get element that needs to be processed openlist.pop_back(); // remove last element, because it gets processed now resultset.push_back(current); // add current element to resultset auto it = relation.find(current); // get parents of current node if ((it == relation.cend()) // if node not found in list || (it->second.IsNull()) // or no set of parents available || (it->second->Size() == 0)) // or empty set of parents continue; // then continue with next node in open list else for (SetOfObjects::ConstIterator parentIt = it->second->Begin(); parentIt != it->second->End(); ++parentIt) // for each parent of current node { mitk::DataNode::ConstPointer p = parentIt.Value().GetPointer(); if (!(std::find(resultset.cbegin(), resultset.cend(), p) != resultset.end()) // if it is not already in resultset && !(std::find(openlist.cbegin(), openlist.cend(), p) != openlist.cend())) // and not already in openlist openlist.push_back(p); // then add it to openlist, so that it can be processed } } /* now finally copy the results to a proper SetOfObjects variable exluding the initial node and checking the condition * if any is given */ mitk::DataStorage::SetOfObjects::Pointer realResultset = mitk::DataStorage::SetOfObjects::New(); if (condition != nullptr) { for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if ((*resultIt != node) && (condition->CheckNode(*resultIt) == true)) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } else { for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if (*resultIt != node) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } return SetOfObjects::ConstPointer(realResultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetSources( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectSources) const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); return this->GetRelations(node, m_SourceNodes, condition, onlyDirectSources); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetDerivations( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectDerivations) const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); return this->GetRelations(node, m_DerivedNodes, condition, onlyDirectDerivations); } void mitk::StandaloneDataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << "StandaloneDataStorage:\n"; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index dcf50ecff8..dba208e86f 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,754 +1,754 @@ /*============================================================================ 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 "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName) { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType) { if ("GE4ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4"); } else if ("GE5ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5"); } else if ("Bruker2dseqImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq"); } } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } auto extensions = customReaderMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customReaderMimeType); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } auto extensions = customWriterMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customWriterMimeType); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const auto* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry) { std::stringstream stream; stream << timeGeometry->GetTimeBounds(0)[0]; const auto maxTimePoints = timeGeometry->CountTimeSteps(); for (TimeStepType pos = 0; pos < maxTimePoints; ++pos) { auto timeBounds = timeGeometry->GetTimeBounds(pos); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed as soon as T28262 is solved! if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1]) { timeBounds[1] = timeBounds[0] + 1.; } // End of workarround for T27883 ////////////////////////////////////// stream << " " << timeBounds[1]; } auto result = itk::MetaDataObject::New(); result->SetMetaDataObjectValue(stream.str()); return result.GetPointer(); }; std::vector ItkImageIO::DoRead() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); if (loadedProp.IsNull()) { MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; break; } image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(image); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); - m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? - static_cast(pixelType.GetComponentType()) : - itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); + m_ImageIO->SetComponentType(static_cast(pixelType.GetComponentType()) < PixelComponentUserType + ? pixelType.GetComponentType() + : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING; try { value = infoList.front()->GetSerializationFunction()(property.second); } catch (const std::exception& e) { MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what(); } catch (...) { MITK_ERROR << "Unkown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first; } if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID()); ImageReadAccessor imageAccess(image); LocaleSwitch localeSwitch2("C"); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/src/IO/mitkLog.cpp b/Modules/Core/src/IO/mitkLog.cpp index de655bfa80..64fbe3a00a 100644 --- a/Modules/Core/src/IO/mitkLog.cpp +++ b/Modules/Core/src/IO/mitkLog.cpp @@ -1,249 +1,249 @@ /*============================================================================ 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 #include #include +#include -static itk::SimpleFastMutexLock logMutex; +static std::mutex logMutex; static mitk::LoggingBackend *mitkLogBackend = nullptr; static std::ofstream *logFile = nullptr; static std::string logFileName = ""; static std::stringstream *outputWindow = nullptr; static bool logOutputWindow = false; void mitk::LoggingBackend::EnableAdditionalConsoleWindow(bool enable) { logOutputWindow = enable; } void mitk::LoggingBackend::ProcessMessage(const mbilog::LogMessage &l) { - logMutex.Lock(); + logMutex.lock(); #ifdef _WIN32 FormatSmart(l, (int)GetCurrentThreadId()); #else FormatSmart(l); #endif if (logFile) { #ifdef _WIN32 FormatFull(*logFile, l, (int)GetCurrentThreadId()); #else FormatFull(*logFile, l); #endif } if (logOutputWindow) { if (outputWindow == nullptr) { outputWindow = new std::stringstream(); } outputWindow->str(""); outputWindow->clear(); #ifdef _WIN32 FormatFull(*outputWindow, l, (int)GetCurrentThreadId()); #else FormatFull(*outputWindow, l); #endif itk::OutputWindow::GetInstance()->DisplayText(outputWindow->str().c_str()); } - logMutex.Unlock(); + logMutex.unlock(); } void mitk::LoggingBackend::Register() { if (mitkLogBackend) return; mitkLogBackend = new mitk::LoggingBackend(); mbilog::RegisterBackend(mitkLogBackend); } void mitk::LoggingBackend::Unregister() { if (mitkLogBackend) { SetLogFile(nullptr); mbilog::UnregisterBackend(mitkLogBackend); delete mitkLogBackend; mitkLogBackend = nullptr; } } void mitk::LoggingBackend::SetLogFile(const char *file) { // closing old logfile { bool closed = false; std::string closedFileName; - logMutex.Lock(); + logMutex.lock(); if (logFile) { closed = true; closedFileName = logFileName; logFile->close(); delete logFile; logFile = nullptr; logFileName = ""; } - logMutex.Unlock(); + logMutex.unlock(); if (closed) { MITK_INFO << "closing logfile (" << closedFileName << ")"; } } // opening new logfile if (file) { - logMutex.Lock(); + logMutex.lock(); logFileName = file; logFile = new std::ofstream(); logFile->open(file, std::ios_base::out | std::ios_base::app); if (logFile->good()) { - logMutex.Unlock(); + logMutex.unlock(); MITK_INFO << "Logfile: " << logFileName; } else { delete logFile; logFile = nullptr; - logMutex.Unlock(); + logMutex.unlock(); MITK_WARN << "opening logfile '" << file << "' for writing failed"; } // mutex is now unlocked } } std::string mitk::LoggingBackend::GetLogFile() { return logFileName; } void mitk::LoggingBackend::CatchLogFileCommandLineParameter(int &argc, char **argv) { int r; for (r = 1; r < argc; r++) { if (std::string(argv[r]) == "--logfile") { if (r + 1 >= argc) { --argc; MITK_ERROR << "--logfile parameter found, but no file given"; return; } mitk::LoggingBackend::SetLogFile(argv[r + 1]); for (r += 2; r < argc; r++) argv[r - 2] = argv[r]; argc -= 2; return; } } } void mitk::LoggingBackend::RotateLogFiles(const std::string &prefixPath) { static const int numLogFiles = 10; std::string newEmptyLogFileName; // first: rotate the old log files to get a new, free logfile name newEmptyLogFileName = IncrementLogFileNames(prefixPath, numLogFiles); // now: use the new empty logfile name as name for this run mitk::LoggingBackend::SetLogFile(newEmptyLogFileName.c_str()); } std::string mitk::LoggingBackend::IncrementLogFileNames(const std::string &prefixPath, int numLogFiles) { // delete last one { std::stringstream s; s << prefixPath.c_str() << "-" << numLogFiles - 1 << ".log"; // check if the file exists if (CheckIfFileExists(s.str())) // if yes: delete it { int retVal = ::remove(s.str().c_str()); if (retVal != 0) { mitkThrow() << "Problem while deleting the oldest log file. Maybe the access to this files is blocked. Aborting!"; } } } // rename the others for (int r = numLogFiles - 1; r >= 1; r--) { std::stringstream dst; dst << prefixPath.c_str() << "-" << r << ".log"; std::stringstream src; src << prefixPath.c_str() << "-" << r - 1 << ".log"; // check if the source exists if (CheckIfFileExists(src.str())) // if yes: rename it { int retVal = ::rename(src.str().c_str(), dst.str().c_str()); if (retVal != 0) { mitkThrow() << "Problem while renaming the log files. Maybe the access to this files is blocked. Aborting!"; } } } // create new empty name and return it { std::stringstream s; s << prefixPath.c_str() << "-0.log"; return s.str(); } } bool mitk::LoggingBackend::CheckIfFileExists(const std::string &filename) { bool returnValue = false; std::ifstream File(filename.c_str()); if (File.good()) { returnValue = true; } else { returnValue = false; } File.close(); return returnValue; } mbilog::OutputType mitk::LoggingBackend::GetOutputType() const { return mbilog::Console; } diff --git a/Modules/Core/src/IO/mitkPixelType.cpp b/Modules/Core/src/IO/mitkPixelType.cpp index 93c47fee9e..0d4c304d91 100644 --- a/Modules/Core/src/IO/mitkPixelType.cpp +++ b/Modules/Core/src/IO/mitkPixelType.cpp @@ -1,178 +1,178 @@ /*============================================================================ 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 "mitkPixelType.h" #include mitk::PixelType::PixelType(const mitk::PixelType &other) : m_ComponentType(other.m_ComponentType), m_PixelType(other.m_PixelType), m_ComponentTypeName(other.m_ComponentTypeName), m_PixelTypeName(other.m_PixelTypeName), m_NumberOfComponents(other.m_NumberOfComponents), m_BytesPerComponent(other.m_BytesPerComponent) { } mitk::PixelType &mitk::PixelType::operator=(const PixelType &other) { m_ComponentType = other.m_ComponentType; m_PixelType = other.m_PixelType; m_ComponentTypeName = other.m_ComponentTypeName; m_PixelTypeName = other.m_PixelTypeName; m_NumberOfComponents = other.m_NumberOfComponents; m_BytesPerComponent = other.m_BytesPerComponent; return *this; } -itk::ImageIOBase::IOPixelType mitk::PixelType::GetPixelType() const +mitk::PixelType::ItkIOPixelType mitk::PixelType::GetPixelType() const { return m_PixelType; } -int mitk::PixelType::GetComponentType() const +mitk::PixelType::ItkIOComponentType mitk::PixelType::GetComponentType() const { return m_ComponentType; } std::string mitk::PixelType::GetPixelTypeAsString() const { return m_PixelTypeName; } std::string mitk::PixelType::GetComponentTypeAsString() const { return m_ComponentTypeName; } std::string mitk::PixelType::GetTypeAsString() const { return m_PixelTypeName + " (" + m_ComponentTypeName + ")"; } size_t mitk::PixelType::GetSize() const { return (m_NumberOfComponents * m_BytesPerComponent); } size_t mitk::PixelType::GetBpe() const { return this->GetSize() * 8; } size_t mitk::PixelType::GetNumberOfComponents() const { return m_NumberOfComponents; } size_t mitk::PixelType::GetBitsPerComponent() const { return m_BytesPerComponent * 8; } mitk::PixelType::~PixelType() { } -mitk::PixelType::PixelType(const int componentType, - const ItkIOPixelType pixelType, +mitk::PixelType::PixelType(ItkIOComponentType componentType, + ItkIOPixelType pixelType, std::size_t bytesPerComponent, std::size_t numberOfComponents, const std::string &componentTypeName, const std::string &pixelTypeName) : m_ComponentType(componentType), m_PixelType(pixelType), m_ComponentTypeName(componentTypeName), m_PixelTypeName(pixelTypeName), m_NumberOfComponents(numberOfComponents), m_BytesPerComponent(bytesPerComponent) { } bool mitk::PixelType::operator==(const mitk::PixelType &rhs) const { bool returnValue = ( this->m_PixelType == rhs.m_PixelType && this->m_ComponentType == rhs.m_ComponentType && this->m_NumberOfComponents == rhs.m_NumberOfComponents && this->m_BytesPerComponent == rhs.m_BytesPerComponent ); MITK_DEBUG << "|> mitk::PixelType::operator== rhs, lhs: \n" << "| m_BytesPerComponent = " << m_BytesPerComponent << ", " << rhs.m_BytesPerComponent << '\n' << "| m_NumberOfComponents = " << m_NumberOfComponents << ", " << rhs.m_NumberOfComponents << '\n' << "| m_PixelTypeName = " << m_PixelTypeName << ", " << rhs.m_PixelTypeName << '\n' << "| m_ComponentTypeName = " << m_ComponentTypeName << ", " << rhs.m_ComponentTypeName << '\n' << "| m_PixelType = " << m_PixelType << ", " << rhs.m_PixelType << '\n' << "| m_ComponentType = " << m_ComponentType << ", " << rhs.m_ComponentType << ", returnValue = " << returnValue << (returnValue ? "[True]" : "[False]") << ". <|"; return returnValue; } bool mitk::PixelType::operator!=(const mitk::PixelType &rhs) const { return !(this->operator==(rhs)); } mitk::PixelType mitk::MakePixelType(vtkImageData *vtkimagedata) { int numOfComponents = vtkimagedata->GetNumberOfScalarComponents(); switch (vtkimagedata->GetScalarType()) { case VTK_BIT: case VTK_CHAR: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_CHAR: return mitk::MakePixelType(numOfComponents); break; case VTK_SHORT: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_SHORT: return mitk::MakePixelType(numOfComponents); break; case VTK_INT: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_INT: return mitk::MakePixelType(numOfComponents); break; case VTK_LONG: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_LONG: return mitk::MakePixelType(numOfComponents); break; case VTK_FLOAT: return mitk::MakePixelType(numOfComponents); break; case VTK_DOUBLE: return mitk::MakePixelType(numOfComponents); break; default: break; } mitkThrow() << "tried to make pixeltype from vtkimage of unknown data type(short, char, int, ...)"; } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp index fb52e35151..44f1eba772 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -1,914 +1,914 @@ /*============================================================================ 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 "mitkDisplayActionEventBroadcast.h" // us #include "usGetModuleContext.h" #include "usModuleContext.h" // mitk core module #include #include #include #include #include #include #include #include #include #include #include #include mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast() : m_AlwaysReact(false) , m_AutoRepeat(false) , m_IndexToSliceModifier(4) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_ZoomFactor(2) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); // register the broadcast class (itself) as an interaction event observer via micro services us::ServiceProperties props; props["name"] = std::string("DisplayActionEventBroadcast"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(this, props); } mitk::DisplayActionEventBroadcast::~DisplayActionEventBroadcast() { m_ServiceRegistration.Unregister(); } void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled) { // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } void mitk::DisplayActionEventBroadcast::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } void mitk::DisplayActionEventBroadcast::ConfigurationChanged() { PropertyList::Pointer properties = GetAttributes(); // allwaysReact std::string strAlwaysReact = ""; m_AlwaysReact = false; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } } // auto repeat std::string strAutoRepeat = ""; m_AutoRepeat = false; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; m_IndexToSliceModifier = 4; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; m_LinkPlanes = false; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") { m_LinkPlanes = true; } } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } } bool mitk::DisplayActionEventBroadcast::FilterEvents(InteractionEvent* interactionEvent, DataNode * /*dataNode*/) { BaseRenderer* sendingRenderer = interactionEvent->GetSender(); if (nullptr == sendingRenderer) { return false; } if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID()) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckRotationPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO intersection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const PlaneGeometry* rendererWorldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == rendererWorldPlaneGeometry) { return false; } Point3D position = positionEvent->GetPositionInWorld(); const auto spacing = rendererWorldPlaneGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const ScalarType threshholdDistancePixels = 12.0; auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } // check if there is an intersection between rendered / clicked geometry and the one being analyzed Line3D intersectionLine; if (!rendererWorldPlaneGeometry->IntersectionLine(rendererPlaneGeometry, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(position) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { // we just take the last one, so overwrite each iteration (we just need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used anyOtherGeometry = rendererPlaneGeometry; if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (nullptr == geometryToBeRotated) // first one close to the cursor { geometryToBeRotated = rendererPlaneGeometry; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && rendererWorldPlaneGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we have enough information for rotation // remember where the last cursor position ON THE LINE has been observed m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(position); // find center of rotation by intersection with any of the OTHER lines if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) { return true; } else { return false; } } return false; } bool mitk::DisplayActionEventBroadcast::CheckSwivelPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const Point3D& position = positionEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry* clickedGeometry(nullptr); const PlaneGeometry* otherGeometry1(nullptr); const PlaneGeometry* otherGeometry2(nullptr); const ScalarType threshholdDistancePixels = 6.0; auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = rendererPlaneGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (nullptr == otherGeometry1) { otherGeometry1 = rendererPlaneGeometry; } else { otherGeometry2 = rendererPlaneGeometry; } if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } Line3D line; Point3D point; if ((nullptr != clickedGeometry) && (nullptr != otherGeometry1) && (nullptr != otherGeometry2) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(position) < threshholdDistancePixels) { return false; } else { m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = { 1.0, 0.0, 0.0 }; ScalarType yVector[] = { 0.0, 1.0, 0.0 }; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer* sender = interactionEvent->GetSender(); Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen(); if (m_InvertMoveDirection) { moveVector *= -1.0; } moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here? // store new display coordinate m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate move event with computed geometry values InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector)); } void mitk::DisplayActionEventBroadcast::SetCrosshair(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); // propagate set crosshair event with computed geometry values InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position)); } void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate zoom event with computed geometry values InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); } void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } int sliceDelta = 0; // scroll direction if (m_ScrollDirection == "updown") { sliceDelta = static_cast(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]); } else { sliceDelta = static_cast(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]); } if (m_InvertScrollDirection) { sliceDelta *= -1; } // set how many pixels the mouse has to be moved to scroll one slice // if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier) { sliceDelta = m_IndexToSliceModifier; } else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier) { sliceDelta = -m_IndexToSliceModifier; } sliceDelta /= m_IndexToSliceModifier; // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate scroll event with computed geometry values InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = 1; if (m_InvertScrollDirection) { sliceDelta = -1; } // propagate scroll event with a single slice delta (increase) InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = -1; if (m_InvertScrollDirection) { sliceDelta = 1; } // propagate scroll event with a single slice delta (decrease) InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } ScalarType level; ScalarType window; if (m_LevelDirection == "leftright") { level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertLevelWindowDirection) { level *= -1; window *= -1; } level *= static_cast(2); window *= static_cast(2); // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate set level window event with the level and window delta InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window)); } void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { ResetMouseCursor(); } void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = position - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = position; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } // Determine relative mouse movement projected onto world space Point2D position = positionEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = position - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayActionEventBroadcast::IncreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayActionEventBroadcast::DecreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer::Pointer renderer = positionEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNull()) { return; } Point3D worldposition; renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); auto globalCurrentTimePoint = renderer->GetTime(); Image::Pointer image3D; DataNode::Pointer node; DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); if (node.IsNull()) { return; } bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } // get the position and pixel value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); - if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) + if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } - else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) + else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } else { ScalarType pixelValue; mitkPixelTypeMultiplex5( FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(renderer->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } bool mitk::DisplayActionEventBroadcast::GetBoolProperty(PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index bb6a9ae2d8..422e594902 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,944 +1,944 @@ /*============================================================================ 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 "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkStandaloneDataStorage.h" #include "vtkRenderWindowInteractor.h" // Rotation #include "mitkInteractionConst.h" #include "rotate_cursor.xpm" #include #include #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkStatusBar.h" #include void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4) , m_AutoRepeat(false) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_AlwaysReact(false) , m_ZoomFactor(2) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { // nothing here } void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto *posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer *clickedRenderer = posEvent->GetSender(); const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == nullptr) continue; // ignore, we don't see a plane // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just // need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == nullptr) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project( cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint( intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry(nullptr); const PlaneGeometry *otherGeometry1(nullptr); const PlaneGeometry *otherGeometry2(nullptr); auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = {1.0, 0.0, 0.0}; ScalarType yVector[] = {0.0, 1.0, 0.0}; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer *sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if (m_InvertMoveDirection) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); sender->GetCameraController()->MoveBy(moveVector); RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { auto* positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) { BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } } void mitk::DisplayInteractor::IncreaseTimeStep(StateMachineAction *, InteractionEvent *) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayInteractor::DecreaseTimeStep(StateMachineAction *, InteractionEvent *) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent) { float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } auto* positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); if (factor != 1.0) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow()); } } void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent) { auto* positionEvent = static_cast(interactionEvent); mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if (m_InvertScrollDirection) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // set the new position sliceNaviController->GetSlice()->SetPos(newPos); } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); ++i) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if (m_LevelDirection != "leftright") { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if (m_InvertLevelWindowDirection) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto* posEvent = dynamic_cast(event); if (nullptr == posEvent) { return; } const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); auto globalCurrentTimePoint = baseRenderer->GetTime(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNull()) { return; } // posEvent->GetPositionInWorld() would return the world position at the // time of initiating the interaction. However, we need to update the // status bar with the position after changing slice. Therefore, we // translate the same display position with the renderer again to // get the new world position. Point3D worldposition; baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer); if (node.IsNull()) { return; } bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, baseRenderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } // get the position and gray value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); - if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) + if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); } - else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) + else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); } else { mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index 5e4e797c38..12555a0d66 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1153 +1,1153 @@ /*============================================================================ 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. ============================================================================*/ // MITK #include #include #include #include #include #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include namespace { bool IsBinaryImage(mitk::Image* image) { if (nullptr != image && image->IsInitialized()) { bool isBinary = true; auto statistics = image->GetStatistics(); const auto numTimeSteps = image->GetTimeSteps(); for (std::remove_const_t t = 0; t < numTimeSteps; ++t) { const auto numChannels = image->GetNumberOfChannels(); for (std::remove_const_t c = 0; c < numChannels; ++c) { auto minValue = statistics->GetScalarValueMin(t, c); auto maxValue = statistics->GetScalarValueMax(t, c); if (std::abs(maxValue - minValue) < mitk::eps) continue; auto min2ndValue = statistics->GetScalarValue2ndMin(t, c); auto max2ndValue = statistics->GetScalarValue2ndMax(t, c); if (std::abs(maxValue - min2ndValue) < mitk::eps && std::abs(max2ndValue - minValue) < mitk::eps) continue; isBinary = false; break; } if (!isBinary) break; } return isBinary; } return false; } } mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_PublicActors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { this->SetToInvalidState(localStorage); return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { this->SetToInvalidState(localStorage); return; } image->Update(); localStorage->m_PublicActors = localStorage->m_Actors.Get(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { this->SetToInvalidState(localStorage); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const auto *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const auto *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image - itk::ImageIOBase::IOComponentType componentType = static_cast(image->GetPixelType().GetComponentType()); + auto componentType = image->GetPixelType().GetComponentType(); switch (componentType) { - case itk::ImageIOBase::UCHAR: + case itk::IOComponentEnum::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; - case itk::ImageIOBase::USHORT: + case itk::IOComponentEnum::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); localStorage->m_ShadowOutlineActor->GetProperty()->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); localStorage->m_ImageActor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_ImageActor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { localStorage->m_ShadowOutlineActor->SetVisibility(true); } else { localStorage->m_ShadowOutlineActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_ImageActor->SetTexture(localStorage->m_Texture); localStorage->m_ShadowOutlineActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(rgbConv); localStorage->m_ImageActor->GetProperty()->SetColor(rgbConv); float shadowRGB[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(shadowRGB, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double shadowRGBConv[3] = {(double)shadowRGB[0], (double)shadowRGB[1], (double)shadowRGB[2]}; // conversion to double for VTK localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(shadowRGBConv); } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_ImageActor->GetProperty()->SetOpacity(opacity); localStorage->m_ShadowOutlineActor->GetProperty()->SetOpacity(opacity); } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable", renderer)); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::SetToInvalidState(mitk::ImageVtkMapper2D::LocalStorage* localStorage) { localStorage->m_PublicActors = localStorage->m_EmptyActors.Get(); // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } auto *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { this->SetToInvalidState(localStorage); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPropertyList()->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp, renderer); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp, renderer); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); isBinaryImage = IsBinaryImage(centralSliceImage); if (isBinaryImage) // Potential binary image. Now take a close look. isBinaryImage = IsBinaryImage(image); } std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); - if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || + if ((pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ mitk::LevelWindow contrast; std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // fallback } contrast.SetLevelWindow(level, window, true); } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // fallback } node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && - (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && - (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) + (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::RGBA) && + (image->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } const mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetConstLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image auto* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_ImageActor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_ImageActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); localStorage->m_ShadowOutlineActor->SetUserTransform(trans); localStorage->m_ShadowOutlineActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_ImageActor = vtkSmartPointer::New(); m_ShadowOutlineActor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_EmptyActors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_ImageActor->SetMapper(m_Mapper); m_ShadowOutlineActor->SetMapper(m_Mapper); m_Actors->AddPart(m_ShadowOutlineActor); m_Actors->AddPart(m_ImageActor); m_PublicActors = m_EmptyActors.Get(); } diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp index 4470ec796c..acf5c6963f 100644 --- a/Modules/Core/src/mitkCoreServices.cpp +++ b/Modules/Core/src/mitkCoreServices.cpp @@ -1,128 +1,127 @@ /*============================================================================ 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 "mitkCoreServices.h" #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include namespace mitk { - itk::SimpleFastMutexLock &s_ContextToServicesMapMutex() + std::mutex &s_ContextToServicesMapMutex() { - static itk::SimpleFastMutexLock mutex; + static std::mutex mutex; return mutex; } std::map> &s_ContextToServicesMap() { static std::map> serviceMap; return serviceMap; } template static S *GetCoreService(us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); S *coreService = nullptr; us::ServiceReference serviceRef = context->GetServiceReference(); if (serviceRef) { coreService = context->GetService(serviceRef); } assert(coreService && "Asserting non-nullptr MITK core service"); { - itk::MutexLockHolder l(s_ContextToServicesMapMutex()); + std::lock_guard l(s_ContextToServicesMapMutex()); s_ContextToServicesMap()[context].insert(std::make_pair(coreService, serviceRef)); } return coreService; } IPropertyAliases *CoreServices::GetPropertyAliases(us::ModuleContext *context) { return GetCoreService(context); } IPropertyDescriptions *CoreServices::GetPropertyDescriptions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyFilters *CoreServices::GetPropertyFilters(us::ModuleContext *context) { return GetCoreService(context); } IPropertyPersistence *CoreServices::GetPropertyPersistence(us::ModuleContext *context) { return GetCoreService(context); } IPropertyRelations *CoreServices::GetPropertyRelations(us::ModuleContext *context) { return GetCoreService(context); } IMimeTypeProvider *CoreServices::GetMimeTypeProvider(us::ModuleContext *context) { return GetCoreService(context); } bool CoreServices::Unget(us::ModuleContext *context, const std::string & /*interfaceId*/, void *service) { bool success = false; - itk::MutexLockHolder l(s_ContextToServicesMapMutex()); + std::lock_guard l(s_ContextToServicesMapMutex()); auto iter = s_ContextToServicesMap().find(context); if (iter != s_ContextToServicesMap().end()) { auto iter2 = iter->second.find(service); if (iter2 != iter->second.end()) { us::ServiceReferenceU serviceRef = iter2->second; if (serviceRef) { success = context->UngetService(serviceRef); if (success) { iter->second.erase(iter2); } } } } return success; } } diff --git a/Modules/Core/test/mitkImageGeneratorTest.cpp b/Modules/Core/test/mitkImageGeneratorTest.cpp index 57dc747de6..cc106d629a 100644 --- a/Modules/Core/test/mitkImageGeneratorTest.cpp +++ b/Modules/Core/test/mitkImageGeneratorTest.cpp @@ -1,327 +1,327 @@ /*============================================================================ 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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include // MITK includes #include #include "mitkImage.h" #include "mitkImageGenerator.h" #include "mitkImageReadAccessor.h" #include "mitkImageStatisticsHolder.h" class mitkImageGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageGeneratorTestSuite); MITK_TEST(SetSpacingX2D_Success); MITK_TEST(SetDefaultSpacingX2D_Success); MITK_TEST(SetSpacingX3D_Success); MITK_TEST(SetSpacingY2D_Success); MITK_TEST(SetDefaultSpacingY2D_Success); MITK_TEST(SetSpacingY3D_Success); MITK_TEST(SetSpacingZ2D_Success); MITK_TEST(SetDefaultSpacingZ2D_Success); MITK_TEST(SetSpacingZ3D_Success); MITK_TEST(SetDimension2D_Success); MITK_TEST(SetDimension3D_Success); MITK_TEST(SetDimension4D_Success); MITK_TEST(SetDimensionX2D_Success); MITK_TEST(SetDimensionY2D_Success); MITK_TEST(SetDimensionZ3D_Success); MITK_TEST(SetDimensionT4D_Success); MITK_TEST(SetDimensions3Dc_Success); MITK_TEST(SetDataTypeFloat2D_Success); MITK_TEST(SetDataTypeUChar2D_Success); MITK_TEST(SetDataTypeInt3D_Success); MITK_TEST(SetDataTypeDouble3D_Success); MITK_TEST(SetDataTypeFloat4D_Success); MITK_TEST(SetDataTypeUChar4D_Success); MITK_TEST(SetDataTypeUInt3D_Success); MITK_TEST(SetPixelTypeFloat2D_Success); MITK_TEST(SetPixelTypeUChar2D_Success); MITK_TEST(SetPixelTypeInt3D_Success); MITK_TEST(SetPixelTypeDouble3D_Success); MITK_TEST(SetPixelTypeFloat4D_Success); MITK_TEST(SetPixelTypeUChar4D_Success); MITK_TEST(SetPixelTypeUInt3D_Success); MITK_TEST(MaxValueHolds_Success); MITK_TEST(MinValueHolds_Success); MITK_TEST(DefaultMaxValueHolds_Success); MITK_TEST(DefaultMinValueHolds_Success); MITK_TEST(SetGradientImageValues_Success); CPPUNIT_TEST_SUITE_END(); private: // create some images with arbitrary parameters (corner cases) mitk::Image::Pointer m_Image2Da; mitk::Image::Pointer m_Image2Db; mitk::Image::Pointer m_Image3Da; mitk::Image::Pointer m_Image3Db; mitk::Image::Pointer m_Image4Da; mitk::Image::Pointer m_Image4Db; mitk::Image::Pointer m_Image3Dc; public: void setUp() { m_Image2Da = mitk::ImageGenerator::GenerateRandomImage(2, 4, 0, 0, 0.1, 0.2, 0.3, 577, 23); m_Image2Db = mitk::ImageGenerator::GenerateRandomImage(1, 1, 0, 0); m_Image3Da = mitk::ImageGenerator::GenerateRandomImage(2, 4, 1, 0); m_Image3Db = mitk::ImageGenerator::GenerateRandomImage(2, 4, 8, 0); m_Image4Da = mitk::ImageGenerator::GenerateRandomImage(2, 4, 8, 1); m_Image4Db = mitk::ImageGenerator::GenerateRandomImage(2, 4, 8, 2); m_Image3Dc = mitk::ImageGenerator::GenerateGradientImage(1, 2, 3, 4, 5, 6); } void tearDown() { m_Image2Da = nullptr; m_Image2Db = nullptr; m_Image3Da = nullptr; m_Image3Db = nullptr; m_Image4Da = nullptr; m_Image4Db = nullptr; m_Image3Dc = nullptr; } void SetSpacingX2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D x is set correctly.", fabs(m_Image2Da->GetGeometry()->GetSpacing()[0] - 0.1) < 0.0001); } void SetDefaultSpacingX2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D x is set correctly.", fabs(m_Image2Db->GetGeometry()->GetSpacing()[0] - 1.0) < 0.0001); } void SetSpacingX3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 3D x is set correctly.", fabs(m_Image3Dc->GetGeometry()->GetSpacing()[0] - 4) < 0.0001); } void SetSpacingY2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D y is set correctly.", fabs(m_Image2Da->GetGeometry()->GetSpacing()[1] - 0.2) < 0.0001); } void SetDefaultSpacingY2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D y is set correctly.", fabs(m_Image2Db->GetGeometry()->GetSpacing()[1] - 1.0) < 0.0001); } void SetSpacingY3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 3D y is set correctly.", fabs(m_Image3Dc->GetGeometry()->GetSpacing()[1] - 5) < 0.0001); } void SetSpacingZ2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D z is set correctly.", fabs(m_Image2Da->GetGeometry()->GetSpacing()[2] - 0.3) < 0.0001); } void SetDefaultSpacingZ2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D z is set correctly.", fabs(m_Image2Db->GetGeometry()->GetSpacing()[2] - 1.0) < 0.0001); } void SetSpacingZ3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing z is set correctly.", fabs(m_Image3Dc->GetGeometry()->GetSpacing()[2] - 6) < 0.0001); } void SetDimension2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 2D is set correctly.", m_Image2Da->GetDimension() == 2); CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 2D is set correctly.", m_Image2Db->GetDimension() == 2); } void SetDimension3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 3D is set correctly.",m_Image3Da->GetDimension() == 2); CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 3D is set correctly.", m_Image3Db->GetDimension() == 3); } void SetDimension4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 4D is set correctly.", m_Image4Da->GetDimension() == 3); CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 4D is set correctly.", m_Image4Db->GetDimension() == 4); } void SetDimensionX2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the X dimension of the 2D image is set correctly.", m_Image2Da->GetDimension(0) == 2); } void SetDimensionY2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the Y dimension of the 2D image is set correctly.", m_Image2Db->GetDimension(1) == 1); } void SetDimensionZ3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the Z dimension of the 3D image is set correctly.", m_Image3Da->GetDimension(2) == 1); CPPUNIT_ASSERT_MESSAGE("Testing if the Z dimension of the 3D image is set correctly.", m_Image3Db->GetDimension(2) == 8); } void SetDimensionT4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the T dimension of the 4D image is set correctly.", m_Image4Da->GetDimension(3) == 1); CPPUNIT_ASSERT_MESSAGE("Testing if the T dimension of the 4D image is set correctly.", m_Image4Db->GetDimension(3) == 2); } void SetDimensions3Dc_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension x is set correctly.", m_Image3Dc->GetDimension(0) == 1); CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension y is set correctly.", m_Image3Dc->GetDimension(1) == 2); CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension z is set correctly.", m_Image3Dc->GetDimension(2) == 3); } void SetDataTypeFloat2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a float 3D image is set correctly.", - m_Image2Da->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT); + m_Image2Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT); } void SetDataTypeUChar2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UChar 2D image is set correctly.", - m_Image2Db->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + m_Image2Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); } void SetDataTypeInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Int 3D image is set correctly.", - m_Image3Da->GetPixelType().GetComponentType() == itk::ImageIOBase::INT); + m_Image3Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT); } void SetDataTypeDouble3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Double 3D image is set correctly.", - m_Image3Db->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE); + m_Image3Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::DOUBLE); } void SetDataTypeFloat4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Float 4D image is set correctly.", - m_Image4Da->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT); + m_Image4Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT); } void SetDataTypeUChar4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UChar 4D image is set correctly.", - m_Image4Db->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + m_Image4Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); } void SetDataTypeUInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UInt 3D image is set correctly.", - m_Image3Dc->GetPixelType().GetComponentType() == itk::ImageIOBase::UINT); + m_Image3Dc->GetPixelType().GetComponentType() == itk::IOComponentEnum::UINT); } void SetPixelTypeFloat2D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Float 2D image is set correctly.", m_Image2Da->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeUChar2D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UChar 2D image is set correctly.", m_Image2Db->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeInt3D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Int 3D image is set correctly.", m_Image3Da->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeDouble3D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Double 3D image is set correctly.", m_Image3Db->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeFloat4D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Float 4D image is set correctly.", m_Image4Da->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeUChar4D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UChar 4D image is set correctly.", m_Image4Db->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeUInt3D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UInt 3D image is set correctly.", m_Image3Dc->GetPixelType().GetPixelType() == scalarType); } void MaxValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if max value holds", m_Image2Da->GetStatistics()->GetScalarValueMax() <= 577); } void MinValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if min value holds", m_Image2Da->GetStatistics()->GetScalarValueMin() >= 23); } void DefaultMaxValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default max value holds", m_Image3Da->GetStatistics()->GetScalarValueMax() <= 1000); } void DefaultMinValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default min value holds", m_Image3Da->GetStatistics()->GetScalarValueMin() >= 0); } void SetGradientImageValues_Success() { const unsigned int *image3DcBuffer = nullptr; try { mitk::ImageReadAccessor readAccess(m_Image3Dc); image3DcBuffer = static_cast(readAccess.GetData()); } catch (...) { MITK_ERROR << "Read access not granted on mitk::Image."; } for (unsigned int i = 0; i < 2 * 3; i++) { CPPUNIT_ASSERT_MESSAGE("Testing if gradient image values are set correctly", image3DcBuffer[i] == i); } } }; MITK_TEST_SUITE_REGISTRATION(mitkImageGenerator) diff --git a/Modules/Core/test/mitkImportItkImageTest.cpp b/Modules/Core/test/mitkImportItkImageTest.cpp index 90afbe80f3..cb21e0b207 100644 --- a/Modules/Core/test/mitkImportItkImageTest.cpp +++ b/Modules/Core/test/mitkImportItkImageTest.cpp @@ -1,306 +1,306 @@ /*============================================================================ 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 "mitkITKImageImport.h" #include "mitkImageCast.h" #include "mitkTestingMacros.h" #include "mitkImagePixelReadAccessor.h" #include #include /** * Create a test image with random pixel values. The image size is determined by the input parameter. * * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageRandom(short int size) { typedef typename itk::Image ImageType; itk::Size regionSize; regionSize.Fill(size); typename itk::RandomImageSource::Pointer randomImageSource = itk::RandomImageSource::New(); randomImageSource->SetNumberOfThreads(1); // to produce non-random results randomImageSource->SetSize(regionSize); randomImageSource->Update(); return randomImageSource->GetOutput(); } /** * Create a test vector image (with two components) with a single pixel value. The image size is determined by the input * parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::VectorImage::Pointer CreateTestVectorImageFixedValue( size_t size, const itk::VariableLengthVector &value) { typedef typename itk::VectorImage ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetVectorLength(value.GetNumberOfElements()); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Create a test image with a single pixel value. The image size is determined by the input parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageFixedValue(size_t size, TPixel value) { typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Compares the meta information of both given images for equality. */ template bool Assert_ImageMetaData_AreEqual(typename ImageType::Pointer itkImage, mitk::Image::Pointer mitkImage) { bool return_value = true; typename ImageType::RegionType itkRegion = itkImage->GetLargestPossibleRegion(); typename ImageType::SizeType itkImageSize = itkRegion.GetSize(); // check dimension for (unsigned int idx = 0; idx < mitkImage->GetDimension(); idx++) { return_value &= (itkImageSize[idx] == mitkImage->GetDimension(idx)); } MITK_TEST_CONDITION(return_value, " - Dimensions equal!") // check pixel type bool ptype_compare = (mitkImage->GetPixelType() == mitk::MakePixelType()); return_value &= ptype_compare; MITK_TEST_CONDITION(ptype_compare, " - Pixel types equal!") mitk::BaseGeometry *imageGeometry = mitkImage->GetGeometry(); const mitk::Point3D origin = imageGeometry->GetOrigin(); bool origin_compare = true; for (unsigned int idx = 0; idx < 3; idx++) { origin_compare &= (itkImage->GetOrigin()[idx] == origin[idx]); } return_value &= origin_compare; MITK_TEST_CONDITION(origin_compare, " - Origin equals!") return return_value; } /** * Generates a random itk image and imports it to mitk image through ImportItkImage and compares the values * voxel-wise afterwards */ template void Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: (Random Image, " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageRandom(5); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } /** * Generates an itk image with fixed pixel value and imports it to mitk image through ImportItkImage * and compares the values voxel-wise afterwards */ template void Assert_ItkImageImportSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageFixedValue(5, itk::NumericTraits::min()); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } void Assert_ItkVectorImageImportAndCast_ReturnsTrue() { typedef itk::VectorImage ImageType; ImageType::PixelType value; value.SetSize(2); value.SetElement(0, 1); value.SetElement(0, 2); ImageType::Pointer itkImage = CreateTestVectorImageFixedValue(5, value); mitk::Image::Pointer mitkImage = mitk::ImportItkImage(itkImage); mitk::PixelType pixelType = mitkImage->GetPixelType(); - MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::ImageIOBase::VECTOR, "Vector image pixel type") - MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::ImageIOBase::SHORT, "Vector image component type") + MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR, "Vector image pixel type") + MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::IOComponentEnum::SHORT, "Vector image component type") mitk::Image::Pointer mitkImage2; mitk::CastToMitkImage(itkImage, mitkImage2); mitk::PixelType pixelType2 = mitkImage2->GetPixelType(); MITK_TEST_CONDITION(pixelType == pixelType2, "ImportItkImage and CastToMitkImage produce same pixel types") ImageType::Pointer itkImageOut; mitk::CastToItkImage(mitkImage, itkImageOut); MITK_TEST_CONDITION(pixelType == mitk::MakePixelType(2), "MITK pixel type equals ITK pixel type") typedef itk::VectorImage IntImageType; IntImageType::Pointer itkIntImageOut; mitk::CastToItkImage(mitkImage, itkIntImageOut); MITK_TEST_CONDITION(!(pixelType == mitk::MakePixelType(2)), "MITK pixel type != ITK pixel type") mitk::Image::Pointer mitkImage3; mitk::CastToMitkImage(itkImageOut, mitkImage3); MITK_ASSERT_EQUAL(mitkImage, mitkImage3, "Equality for vector images"); } int mitkImportItkImageTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkImportItkImageTest") Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkVectorImageImportAndCast_ReturnsTrue(); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkPixelTypeTest.cpp b/Modules/Core/test/mitkPixelTypeTest.cpp index 0df8ef5104..3bafd0bff5 100644 --- a/Modules/Core/test/mitkPixelTypeTest.cpp +++ b/Modules/Core/test/mitkPixelTypeTest.cpp @@ -1,241 +1,241 @@ /*============================================================================ 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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include "mitkPixelType.h" #include // ITK includes #include "itkImage.h" #include // VTK includes #include struct MyObscurePixelType { typedef float ValueType; static const size_t Length = 2; }; //## Documentation //## main testing method class mitkPixelTypeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPixelTypeTestSuite); MITK_TEST(GetComponentType_Success); MITK_TEST(GetPixelTypeAsString_Success); MITK_TEST(GetBpePtype_Success); MITK_TEST(GetNumberOfComponentsPtype_Success); MITK_TEST(GetBitsPerComponentPtype_Success); MITK_TEST(GetBpeItkPtype_Success); MITK_TEST(GetNumberOfComponentsItkPtype_Success); MITK_TEST(GetBitsPerComponentItkPtype_Success); MITK_TEST(ConstructorWithBrackets_Success); MITK_TEST(InitializeWithEqualSign_Success); MITK_TEST(EqualityOperator_Success); MITK_TEST(NotEqualityOperator_Success); MITK_TEST(GetPixelTypeUnknown_Success); MITK_TEST(SetLengthCorrectly_Success); MITK_TEST(ValueTypeCorresponds_Success); MITK_TEST(ImageTypeTraitInt3D_Success); MITK_TEST(ImageTypeTraitShort2D_Success); MITK_TEST(ImageTypeTraitVectorShort3D_Success); MITK_TEST(ImageTypeTraitItkInt3D_Success); MITK_TEST(ImageTypeTraitItkShort2D_Success); MITK_TEST(ImageTypeTraitItkVectorShort3D_Success); CPPUNIT_TEST_SUITE_END(); private: typedef itk::Image, 3> ItkImageType; typedef itk::Image MyObscureImageType; typedef itk::VectorImage VectorImageType; typedef itk::Image> FixedVectorImageType; public: void setUp() override { } void tearDown() override { } void GetComponentType_Success() { mitk::PixelType ptype = mitk::MakePixelType(); MITK_INFO << "m_Ptype = mitk::MakePixelType();"; MITK_INFO << "m_ItkPtype = mitk::MakePixelType();\n with itk::Image, 3> ItkImageType"; - CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::ImageIOBase::INT); + CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::IOComponentEnum::INT); } void GetPixelTypeAsString_Success() { mitk::PixelType ptype = mitk::MakePixelType(); MITK_INFO << ptype.GetPixelTypeAsString(); MITK_INFO << typeid(ItkImageType).name(); } void GetBpePtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[m_Ptype] GetBpe()", ptype.GetBpe() == 8 * sizeof(int) * 5); } void GetNumberOfComponentsPtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[ptype]GetNumberOfComponents()", ptype.GetNumberOfComponents() == 5); } void GetBitsPerComponentPtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[ptype]GetBitsPerComponent()", ptype.GetBitsPerComponent() == 8 * sizeof(int)); } void GetBpeItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBpe()", itkPtype.GetBpe() == 8 * sizeof(int) * 5); } void GetNumberOfComponentsItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetNumberOfComponents()", itkPtype.GetNumberOfComponents() == 5); } void GetBitsPerComponentItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBitsPerComponent()", itkPtype.GetBitsPerComponent() == 8 * sizeof(int)); } void ConstructorWithBrackets_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2(ptype); - CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::ImageIOBase::INT); + CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int)); } void InitializeWithEqualSign_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2 = ptype; - CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::ImageIOBase::INT); + CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType()); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int)); } void EqualityOperator_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2 = ptype; CPPUNIT_ASSERT_MESSAGE("operator ==", ptype == ptype2); } void NotEqualityOperator_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype3 = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("operator !=", ptype != ptype3); } void GetPixelTypeUnknown_Success() { // test instantiation mitk::PixelType obscurePixelType = mitk::MakePixelType(); - CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::ImageIOBase::UNKNOWNPIXELTYPE); + CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::IOPixelEnum::UNKNOWNPIXELTYPE); } void SetLengthCorrectly_Success() { mitk::PixelType obscurePixelType = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("Lenght was set correctly", obscurePixelType.GetNumberOfComponents() == MyObscurePixelType::Length); } void ValueTypeCorresponds_Success() { mitk::PixelType obscurePixelType = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("ValueType corresponds.", obscurePixelType.GetComponentType() == mitk::MapPixelComponentType::value); } void ImageTypeTraitInt3D_Success() { // test ImageTypeTrait traits class CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid int 3D equals ITK typeid", typeid(mitk::ImageTypeTrait::ImageType) == typeid(itk::Image)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait::IsVectorImage == false)); } void ImageTypeTraitShort2D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 2D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::Image, 3>)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait, 3>::IsVectorImage == false)); } void ImageTypeTraitVectorShort3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 3D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::VectorImage)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait, 3>::IsVectorImage == true)); } void ImageTypeTraitItkInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK int 3D equals ITK typeid", typeid(mitk::ImageTypeTrait>::ImageType) == typeid(itk::Image)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait>::IsVectorImage == false)); } void ImageTypeTraitItkShort2D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 2D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>>::ImageType) == typeid(itk::Image, 3>)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait, 3>>::IsVectorImage == false)); } void ImageTypeTraitItkVectorShort3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 3D equals ITK typeid", typeid(mitk::ImageTypeTrait>::ImageType) == typeid(itk::VectorImage)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait>::IsVectorImage == true)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPixelType) diff --git a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp index 2139114345..6229d1b1bf 100644 --- a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp +++ b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp @@ -1,253 +1,253 @@ /*============================================================================ 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 "mitkSurfaceToImageFilter.h" #include "mitkTestFixture.h" #include class mitkSurfaceToImageFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSurfaceToImageFilterTestSuite); MITK_TEST(test3DSurfaceValidOutput); MITK_TEST(test3DSurfaceCorrect); MITK_TEST(test3DSurfaceIn4DImage); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Surface::Pointer m_Surface; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Surface = mitk::IOUtil::Load(GetTestDataFilePath("ball.stl")); } void tearDown() override {} void test3DSurfaceValidOutput() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); additionalInputImage->Initialize(mitk::MakeScalarPixelType(), *m_Surface->GetTimeGeometry()); // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", - surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); surfaceToImageFilter->SetUShortBinaryPixelType(true); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", - surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT); + surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT); } void test3DSurfaceCorrect() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); // todo I don't know if this image is always needed. There is no documentation of the filter. Use git blame and ask // the author. mitk::Image::Pointer additionalInputImage = mitk::Image::New(); auto *dims = new unsigned int[3]; dims[0] = 32; dims[1] = 32; dims[2] = 32; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 3, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); // Arrange the filter // The docu does not really tell if this is always needed. Could we skip SetImage in any case? surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<3> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_OutputCorrect", valuesCorrect == true); } void test3DSurfaceIn4DImage() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); auto *dims = new unsigned int[4]; dims[0] = 32; dims[1] = 32; dims[2] = 32; dims[3] = 2; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 4, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); mitk::Image::Pointer secondStep = additionalInputImage->Clone(); unsigned int size = sizeof(unsigned char); for (unsigned int i = 0; i < secondStep->GetDimension(); ++i) { size *= secondStep->GetDimension(i); } { mitk::ImageWriteAccessor accessor(secondStep); memset(accessor.GetData(), 1, size); } additionalInputImage->GetTimeGeometry()->Expand(2); additionalInputImage->GetGeometry(1)->SetSpacing(secondStep->GetGeometry()->GetSpacing()); additionalInputImage->GetGeometry(1)->SetOrigin(secondStep->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry(1)->SetIndexToWorldTransform( secondStep->GetGeometry()->GetIndexToWorldTransform()); { mitk::ImageReadAccessor readAccess(secondStep); additionalInputImage->SetImportVolume(readAccess.GetData(), 0); additionalInputImage->SetImportVolume(readAccess.GetData(), 1); } // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<4> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 hould be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); // Values inside the ball but in the second timestep hould be 0 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_Output4DCorrect", valuesCorrect == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSurfaceToImageFilter)