Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h b/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h
index ddbcd05183..a602085b42 100644
--- a/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h
+++ b/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h
@@ -1,309 +1,309 @@
/*============================================================================
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 mitkAnisotropicIterativeClosestPointRegistration_h
#define mitkAnisotropicIterativeClosestPointRegistration_h
// MITK
#include <mitkCommon.h>
#include <mitkVector.h>
// EXPORTS
#include "MitkAlgorithmsExtExports.h"
// STL
#include <vector>
// ITK
#include <itkMatrix.h>
// forward declarations
class vtkPoints;
class vtkKdTreePointLocator;
namespace mitk
{
class Surface;
class WeightedPointTransform;
/**
* \ingroup AnisotropicRegistration
*
* @brief Implementation of the anisotropic iterative closest point (A-ICP)
- * algoritm.
+ * algorithm.
*
- * This class implements the anisotropic interative closest point (A-ICP)
+ * This class implements the anisotropic iterative closest point (A-ICP)
* algorithm presented in L. Maier-Hein et al. in "Convergent Iterative
- * Closest-Point Algorithm to Accomodate Anisotropic and Inhomogenous
+ * Closest-Point Algorithm to Accommodate Anisotropic and Inhomogenous
* Localization Error.", IEEE T Pattern Anal 34 (8), 1520-1532, 2012.
* The algorithm computes the optimal transformation to align two surfaces.
* In addition to the surfaces a list of covariance matrices is used as
* input for every surface. Each covariance matrix represents the error of
* a specific vertex in the Surface. The covariance matrices
* for each surface can be defined by the user, or calculated
* by the CovarianceMatrixCalculator. In addition a trimmed algorithm version
* is provided to compute the registration of partial overlapping surfaces.
* The algorithm needs a clean surface non manifold edges and without duplicated
* vertices. In addition vtkCleanPolyData can be used to ensure a correct
* Surface representation.
*
* \note The correspondence search is accelerated when OpenMP is enabled.
*
* \b Example:
*
*
* \code
* typedef itk::Matrix < double, 3, 3 > Matrix3x3;
* typedef itk::Vector < double, 3 > Vector3;
* typedef std::vector < Matrix3x3 > CovarianceMatrixList;
*
* // compute the covariance matrices
* mitk::CovarianceMatrixCalculator::Pointer matrixCalculator =
* mitk::CovarianceMatrixCalculator::New();
*
* // compute the covariance matrices for the moving surface (X)
* matrixCalculator->SetInputSurface(movingSurface);
* matrixCalculator->ComputeCovarianceMatrices();
* CovarianceMatrixList sigmas_X = matrixCalculator->GetCovarianceMatrices();
* double meanVarX = matrixCalculator->GetMeanVariance();
*
* // compute the covariance matrices for the fixed surface (Y)
* matrixCalculator->SetInputSurface(fixedSurface);
* matrixCalculator->ComputeCovarianceMatrices();
* CovarianceMatrixList sigmas_Y = matrixCalculator->GetCovarianceMatrices();
* double meanVarY = matrixCalculator->GetMeanVariance();
*
* // the FRE normalization factor
* double normalizationFactor = sqrt( meanVarX + meanVarY);
*
* // A-ICP algorithm
* mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
* mitk::AnisotropicIterativeClosestPointRegistration::New();
*
* // set up parameters
* aICP->SetMovingSurface(movingSurface);
* aICP->SetFixedSurface(fixedSurface);
* aICP->SetCovarianceMatricesMovingSurface(sigmas_X);
* aICP->SetCovarianceMatricesFixedSurface(sigmas_Y);
* aICP->SetFRENormalizationFactor(normalizationFactor);
*
* // Trimming is enabled if a fator > 0.0 is set.
* // 40 percent of the moving point set
* // will be used for registration in this example.
* // To disable trimming set the trim factor back to 0.0
* aICP->SetTrimmFactor(0.4);
*
* // run the algorithm
* aICP->Update();
*
* // retrieve the computed transformation
* Matrix3x3 rotation = aICP->GetRotation();
* Vector3 translation = aICP->GetTranslation();
* \endcode
*
*/
class MITKALGORITHMSEXT_EXPORT AnisotropicIterativeClosestPointRegistration : public itk::Object
{
protected:
/** Definition of a 3x3 covariance matrix.*/
typedef itk::Matrix<double, 3, 3> CovarianceMatrix;
/** Definition of a list of covariance matrices.*/
typedef std::vector<CovarianceMatrix> CovarianceMatrixList;
/** Definition of a translation vector.*/
typedef mitk::Vector3D Translation;
/** Definition of a 3x3 rotation matrix.*/
typedef CovarianceMatrix Rotation;
/** Definition of a correspondeces, index and distance.*/
typedef std::pair<unsigned int, double> Correspondence;
/** Definition of a list of correspondences.*/
typedef std::vector<Correspondence> CorrespondenceList;
AnisotropicIterativeClosestPointRegistration();
~AnisotropicIterativeClosestPointRegistration() override;
/** Max amount of iterations. Default is 1000.*/
unsigned int m_MaxIterations;
/** Threshold used for termination. Default is 1.0e-6.*/
double m_Threshold;
/** Normalization factor for the feducial registration error. default is 0.0.*/
double m_FRENormalizationFactor;
/** Search radius for the correspondence search. Default is 30.*/
double m_SearchRadius;
/** The maximum number of iterations in the weighted point based
* registration. Default is 1000.
*/
double m_MaxIterationsInWeightedPointTransform;
/** The fiducial registration error (FRE).*/
double m_FRE;
/** Trimmfactor for partial overlapping registration. Default is 0.*/
double m_TrimmFactor;
/** Amount of iterations used by the algorithm.*/
unsigned int m_NumberOfIterations;
/** Moving surface that is transformed on the fixed surface.*/
itk::SmartPointer<Surface> m_MovingSurface;
/** The fixed / target surface.*/
itk::SmartPointer<Surface> m_FixedSurface;
/** The weighted point based registration algorithm.*/
itk::SmartPointer<WeightedPointTransform> m_WeightedPointTransform;
/** The covariance matrices belonging to the moving surface (X).*/
CovarianceMatrixList m_CovarianceMatricesMovingSurface;
/** The covariance matrices belonging to the moving surface (Y).*/
CovarianceMatrixList m_CovarianceMatricesFixedSurface;
/** The computed 3x1 translation vector.*/
Translation m_Translation;
/** The computed 3x3 rotation matrix.*/
Rotation m_Rotation;
/**
* Method that computes the correspondences between the moving point set X
* and the fixed point set Y. The distances between the points
* are weighted with weight matrices that are computed from the covariances
* along the surfaces axes. This method implements the runtime optimization
* presented by L. Maier-Hein et al.. The correspondences are computed with
* the help of a kd tree. The correspondences are searched in a given radius
* in the euklidian space. Every correspondence found in this radius is
* weighted based on the covariance matrices and the best weighting will be
* used as a correspondence.
*
* @param X The moving point set.
* @param Z The returned correspondences from the fixed point set.
* @param Y The fixed point set saved in a kd tree.
* @param sigma_X Covariance matrices belonging to the moving point set.
* @param sigma_Y Covariance matrices belonging to the fixed point set.
* @param sigma_Z Covariance matrices belonging to the correspondences found.
* @param correspondences Saved correspondences, in a pair containing the
* their index in Y and distance.
* @param radius The search radius used in in kd tree.
*
*/
void ComputeCorrespondences(vtkPoints *X,
vtkPoints *Z,
vtkKdTreePointLocator *Y,
const CovarianceMatrixList &sigma_X,
const CovarianceMatrixList &sigma_Y,
CovarianceMatrixList &sigma_Z,
CorrespondenceList &correspondences,
const double radius);
public:
mitkClassMacroItkParent(AnisotropicIterativeClosestPointRegistration, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Set the maximum amount of iterations used by the algorithm. */
itkSetMacro(MaxIterations, unsigned int);
/** Set the threshold used to terminate the algorithm.*/
itkSetMacro(Threshold, double);
/** Set the normalization factor for the fiducial registration error (FRE).
* The normalization factor is computed with the help of the mean variance
* of both CovarianceMatrixList that can be obtained when the covariance
* matrices are calculated with the CovarianceMatrixCalculator:
*
* \code{.cpp}
* double FRENormalizationFactor = sqrt ( MeanVarianceX + MeanVarianceY );
* \endcode
*
* if no FRE normalization is used the normalization factor is set to 1.0
* as default value.
*/
itkSetMacro(FRENormalizationFactor, double);
/** Set search radius for the correspondence search.*/
itkSetMacro(SearchRadius, double);
- /** Set the maximim number of iterations used by the point based registration
+ /** Set the maximum number of iterations used by the point based registration
* algorithm.
*/
itkSetMacro(MaxIterationsInWeightedPointTransform, double);
/** Get the fiducial registration error (FRE).*/
itkGetMacro(FRE, double);
/** Get the number of iterations used by the algorithm.*/
itkGetMacro(NumberOfIterations, unsigned int);
/**
* Factor that trimms the point set in percent for
- * partial overlapping surfaces. E.g. 0.4 will use 40 precent
+ * partial overlapping surfaces. E.g. 0.4 will use 40 percent
* of the point set. To enable the trimmed version a
* factor > 0 and < 1 must be set. The default value is 0.0.
*/
itkSetMacro(TrimmFactor, double);
/**
* Set moving surface that includes the point set (X).
*/
itkSetMacro(MovingSurface, itk::SmartPointer<Surface>);
/**
* Set fixed surface that includes the point set (Y).
*/
itkSetMacro(FixedSurface, itk::SmartPointer<Surface>);
/**
* Returns the 3x1 translation vector computed by the algorithm.
*/
itkGetConstReferenceMacro(Translation, Translation);
/**
* Returns the 3x3 rotation matrix computed by the algorithm.
*/
itkGetConstReferenceMacro(Rotation, Rotation);
/**
* Set the covariance matrices of the moving surface. The algorithm
* need the same amount of covariance and points available in the surface.
* The covariance matrix for every vertex in a Surface can be calculated by
* the CovarianceMatrixCalculator. It is also possible to define
* arbitrary matrices by hand.
*/
void SetCovarianceMatricesMovingSurface(CovarianceMatrixList &list)
{
m_CovarianceMatricesMovingSurface = list;
}
/**
* Set the covariance matrices of the fixed surface. The algorithm
* need the same amount of covariance and points available in the surface.
* The covariance matrix for every vertex in a Surface can be calculated by
* the CovarianceMatrixCalculator. It is also possible to define
* arbitrary matrices by hand.
*/
void SetCovarianceMatricesFixedSurface(CovarianceMatrixList &list) { m_CovarianceMatricesFixedSurface = list; }
/**
* This method executes the algorithm.
*
* @warning The algorithm is only a simple calculation filter and can not be
- * used in a mitk filter pipline.
+ * used in a mitk filter pipeline.
*
* @throws Exception if the search radius was doubled more than 20 times to
* prevent endless loops. Re-run the with a different search radius that
* will find the correspondences.
*/
void Update();
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h b/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h
index 098d4774dd..147158dd40 100644
--- a/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h
+++ b/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h
@@ -1,126 +1,126 @@
/*============================================================================
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 mitkAnisotropicRegistrationCommon_h
#define mitkAnisotropicRegistrationCommon_h
#include <itkMatrix.h>
#include <mitkCommon.h>
#include <mitkVector.h>
#include "MitkAlgorithmsExtExports.h"
// forward declarations
class vtkPoints;
namespace mitk
{
class PointSet;
/**
* \ingroup AnisotropicRegistration
* \brief A Class that provides common static functions used by all classes
* and tests in the anisotropic iterative closest point algorithm
* (AnisotropicIterativeClosestPointRegistration).
*
* The class provides common functionality used by the A-ICP algorithm like:
* compute weightmatrices (@ref CalculateWeightMatrix()),
* transform points (@ref TransformPoints()), propagate 3 x 3 matrices
* (@ref PropagateMatrices()) and compute the target registration error (TRE)
* (@ref ComputeTargetRegistrationError()).
*/
class MITKALGORITHMSEXT_EXPORT AnisotropicRegistrationCommon
{
protected:
// local typedefs
/** Definition of the 3 x 3 weight matrix.*/
typedef itk::Matrix<double, 3, 3> WeightMatrix;
/** Definition of a rotation matrix.*/
typedef WeightMatrix Rotation;
/** Definition of the covariance matrix.*/
typedef WeightMatrix CovarianceMatrix;
/** Definition of the translation vector.*/
typedef mitk::Vector3D Translation;
/** Definition of the weight matrix list.*/
typedef std::vector<WeightMatrix> MatrixList;
AnisotropicRegistrationCommon() {}
~AnisotropicRegistrationCommon() {}
public:
/** @brief Method that computes a WeightMatrix with two CovarianceMatrices.
* @param sigma_X CovarianceMatrix from the moving point set.
* @param sigma_Y CovarianceMatrix from the fixed point set.
* @return The computed WeighMatrix.
*/
static WeightMatrix CalculateWeightMatrix(const CovarianceMatrix &sigma_X, const CovarianceMatrix &sigma_Y);
/**
* @brief Transforms a point cloud with a Rotation and Translation.
*
* The method uses two point sets as input. It transforms every point from the
- * source point set and saves the result in the destination. The soure is not
+ * source point set and saves the result in the destination. The source is not
* modified. If the same point set is used as source and destination. The method
* will modify the source and the transformation is done in place.
*
* @warning No bound check is done. Ensure that source and destination are
* allocated and have the same size.
*
* @param src The source point set.
* @param dst The destination point set.
* @param rotation The rotation matrix.
* @param translation The translation vector.
*
*/
static void TransformPoints(vtkPoints *src,
vtkPoints *dst,
const Rotation &rotation,
const Translation &translation);
/**
* @brief Propagate a list of matrices with a rotation matrix.
*
* Method that propagate the source list and saves the result in the destination.
* If the source and destination lists are the same the matrices will be computed
* in place.
*
* @warning No bound check is done. Make sure that both lists are allocated and
* have the same size.
*
* @param src Reference to the source matrices list.
* @param dst Reference to the destination list
* @param rotation Reference to a rotation matrix.
*/
static void PropagateMatrices(const MatrixList &src, MatrixList &dst, const Rotation &rotation);
/**
* @brief Compute the target registration error between two point sets.
*
* Method that is used for testing and evaluation. It computes the target
* registration error (TRE) between two point sets with a rotation matrix and
* a translation vector.
*
* @param movingTargets The target points of the moving point set.
* @param fixedTargets The target points of the fixed point set.
* @param rotation A 3x3 rotation matrix.
* @param translation A 3x1 translation vector.
*
* @return The Target Registration Error (TRE).
*/
static double ComputeTargetRegistrationError(const mitk::PointSet *movingTargets,
const mitk::PointSet *fixedTargets,
const Rotation &rotation,
const Translation &translation);
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h
index 9b88f6c334..3193336193 100644
--- a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h
+++ b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h
@@ -1,141 +1,141 @@
/*============================================================================
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 mitkBoundingObjectCutter_h
#define mitkBoundingObjectCutter_h
#include "MitkAlgorithmsExtExports.h"
#include "itkImage.h"
#include "mitkBoundingObject.h"
#include "mitkCommon.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
namespace mitk
{
//##Documentation
//## @brief Cuts an Boundingobject out of an mitk Image
//##
//## Input Parameters are a mitk::BoundingObject and optionally an mitk::Image
//## if no mitk::Image is provided, the resulting image will have m_InsideValue as pixelvalue on inside pixel,
//## otherwise it will have the pixelvalue of the input image.
//## Pixel on the outside of the BoundingObject will have a pixelvalue of m_OutsideValue
//## \todo What Image resolution/spacing should be used, if no input image is given?
//## @ingroup Process
class MITKALGORITHMSEXT_EXPORT BoundingObjectCutter : public ImageToImageFilter
{
public:
mitkClassMacro(BoundingObjectCutter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void SetBoundingObject(const mitk::BoundingObject *boundingObject);
const mitk::BoundingObject *GetBoundingObject() const;
//##Description
//## @brief Set for inside pixels, used when m_UseInsideValue is @a true
itkSetMacro(InsideValue, ScalarType);
itkGetMacro(InsideValue, ScalarType);
//##Description
//## @brief Set value for outside pixels, used when m_AutoOutsideValue is \a false
itkSetMacro(OutsideValue, ScalarType);
itkGetMacro(OutsideValue, ScalarType);
itkSetMacro(UseInsideValue, bool);
itkGetMacro(UseInsideValue, bool);
itkBooleanMacro(UseInsideValue);
//##Description
- //## @brief If set to \a true the minimum of the ouput pixel type is
+ //## @brief If set to \a true the minimum of the output pixel type is
//## used as outside value.
itkSetMacro(AutoOutsideValue, bool);
itkGetMacro(AutoOutsideValue, bool);
itkBooleanMacro(AutoOutsideValue);
itkGetMacro(InsidePixelCount, unsigned int);
itkGetMacro(OutsidePixelCount, unsigned int);
itkSetMacro(UseWholeInputRegion, bool);
itkGetMacro(UseWholeInputRegion, bool);
protected:
BoundingObjectCutter();
~BoundingObjectCutter() override;
virtual const PixelType GetOutputPixelType();
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
friend void CutImageWithOutputTypeSelect(itk::Image<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep,
TOutputPixel *dummy);
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
friend void CutImageWithOutputTypeSelect(itk::VectorImage<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep,
TOutputPixel *dummy);
template <typename TPixel, unsigned int VImageDimension>
friend void CutImage(itk::Image<TPixel, VImageDimension> *itkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep);
template <typename TPixel, unsigned int VImageDimension>
friend void CutImage(itk::VectorImage<TPixel, VImageDimension> *itkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep);
virtual void ComputeData(mitk::Image *input3D, int boTimeStep);
//##Description
//## @brief BoundingObject that will be cut
mitk::BoundingObject::Pointer m_BoundingObject;
//##Description
//## @brief Value for inside pixels, used when m_UseInsideValue is @a true
//##
//## \sa m_UseInsideValue
ScalarType m_InsideValue;
//##Description
//## @brief Value for outside pixels (default: 0)
//##
//## Used only if m_AutoOutsideValue is \a false.
ScalarType m_OutsideValue;
//##Description
- //## @brief If \a true the minimum of the ouput pixel type is
+ //## @brief If \a true the minimum of the output pixel type is
//## used as outside value (default: \a false)
bool m_AutoOutsideValue;
//##Description
//## @brief Use m_InsideValue for inside pixels (default: \a false)
//##
//## If @a true, pixels that are inside m_BoundingObject
//## will get m_InsideValue in the cutting process
//## If @a false, they keep their original value.
//## \sa m_InsideValue
bool m_UseInsideValue;
unsigned int m_OutsidePixelCount;
unsigned int m_InsidePixelCount;
//##Description
//## @brief Region of input needed for cutting
mitk::SlicedData::RegionType m_InputRequestedRegion;
//##Description
//## @brief Time when Header was last initialized
itk::TimeStamp m_TimeOfHeaderInitialization;
mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
bool m_UseWholeInputRegion;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx
index ab5e26ebbe..dbedb115a6 100644
--- a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx
+++ b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx
@@ -1,276 +1,276 @@
/*============================================================================
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 MITKBOUNDINGOBJECTCUTTER_TXX
#define MITKBOUNDINGOBJECTCUTTER_TXX
#include "itkImageRegionIteratorWithIndex.h"
#include "mitkImageToItk.h"
#include "mitkStatusBar.h"
namespace mitk
{
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
void CutImageWithOutputTypeSelect(itk::Image<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int /* boTimeStep */,
TOutputPixel * /* dummy */)
{
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<TOutputPixel, VImageDimension> ItkOutputImageType;
typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
if (cutter->m_BoundingObject.IsNull())
return;
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
std::cout << " image is NULL...returning" << std::endl;
return;
}
// PART 1: convert m_InputRequestedReg ion (type mitk::SlicedData::RegionType)
// into ITK-image-region (ItkImageType::RegionType)
// unfortunately, we cannot use input->GetRequestedRegion(), because it
// has been destroyed by the mitk::CastToItkImage call of PART 1
// (which sets the m_RequestedRegion to the LargestPossibleRegion).
- // Thus, use our own member m_InputRequestedRegion insead.
+ // Thus, use our own member m_InputRequestedRegion instead.
// first convert the index
typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
itk2vtk(cutter->m_InputRequestedRegion.GetIndex(), tmpIndex);
typename ItkRegionType::IndexType index;
index.SetIndex(tmpIndex);
// then convert the size
typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
itk2vtk(cutter->m_InputRequestedRegion.GetSize(), tmpSize);
typename ItkRegionType::SizeType size;
size.SetSize(tmpSize);
// create the ITK-image-region out of index and size
ItkRegionType inputRegionOfInterest(index, size);
// PART 2: get access to the MITK output image via an ITK image
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(cutter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// PART 3: iterate over input and output using ITK iterators
// create the iterators
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
// Cut the boundingbox out of the image by iterating through
// all pixels and checking if they are inside using IsInside()
cutter->m_OutsidePixelCount = 0;
cutter->m_InsidePixelCount = 0;
mitk::Point3D p;
mitk::BaseGeometry *inputGeometry = cutter->GetInput()->GetGeometry();
TOutputPixel outsideValue;
if (cutter->m_AutoOutsideValue)
{
outsideValue = itk::NumericTraits<TOutputPixel>::min();
}
else
{
outsideValue = (TOutputPixel)cutter->m_OutsideValue;
}
// shall we use a fixed value for each inside pixel?
if (cutter->GetUseInsideValue())
{
TOutputPixel insideValue = (TOutputPixel)cutter->m_InsideValue;
// yes, use a fixed value for each inside pixel (create a binary mask of the bounding object)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set(insideValue);
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
else
{
// no, use the pixel value of the original image (normal cutting)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set((TOutputPixel)inputIt.Value());
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
}
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
void CutImageWithOutputTypeSelect(itk::VectorImage<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int /* boTimeStep */,
TOutputPixel * /* dummy */)
{
typedef itk::VectorImage<TPixel, VImageDimension> ItkInputImageType;
typedef itk::VectorImage<TOutputPixel, VImageDimension> ItkOutputImageType;
typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
if (cutter->m_BoundingObject.IsNull())
return;
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
std::cout << " image is NULL...returning" << std::endl;
return;
}
// PART 1: convert m_InputRequestedReg ion (type mitk::SlicedData::RegionType)
// into ITK-image-region (ItkImageType::RegionType)
// unfortunately, we cannot use input->GetRequestedRegion(), because it
// has been destroyed by the mitk::CastToItkImage call of PART 1
// (which sets the m_RequestedRegion to the LargestPossibleRegion).
- // Thus, use our own member m_InputRequestedRegion insead.
+ // Thus, use our own member m_InputRequestedRegion instead.
// first convert the index
typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
itk2vtk(cutter->m_InputRequestedRegion.GetIndex(), tmpIndex);
typename ItkRegionType::IndexType index;
index.SetIndex(tmpIndex);
// then convert the size
typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
itk2vtk(cutter->m_InputRequestedRegion.GetSize(), tmpSize);
typename ItkRegionType::SizeType size;
size.SetSize(tmpSize);
// create the ITK-image-region out of index and size
ItkRegionType inputRegionOfInterest(index, size);
// PART 2: get access to the MITK output image via an ITK image
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(cutter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// PART 3: iterate over input and output using ITK iterators
// create the iterators
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
// Cut the boundingbox out of the image by iterating through
// all pixels and checking if they are inside using IsInside()
cutter->m_OutsidePixelCount = 0;
cutter->m_InsidePixelCount = 0;
mitk::Point3D p;
mitk::BaseGeometry *inputGeometry = cutter->GetInput()->GetGeometry();
typename ItkOutputImageType::PixelType outsideValue;
outsideValue.SetSize(outputItkImage->GetVectorLength());
if (cutter->m_AutoOutsideValue)
{
outsideValue.Fill(itk::NumericTraits<TOutputPixel>::min());
}
else
{
outsideValue.Fill(cutter->m_OutsideValue);
}
// shall we use a fixed value for each inside pixel?
if (cutter->GetUseInsideValue())
{
typename ItkOutputImageType::PixelType insideValue;
insideValue.Fill(cutter->m_InsideValue);
// yes, use a fixed value for each inside pixel (create a binary mask of the bounding object)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set(insideValue);
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
else
{
// no, use the pixel value of the original image (normal cutting)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set(inputIt.Get());
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
}
template <typename TPixel, unsigned int VImageDimension>
void CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, mitk::BoundingObjectCutter *cutter, int boTimeStep)
{
TPixel *dummy = nullptr;
CutImageWithOutputTypeSelect<TPixel, VImageDimension, TPixel>(inputItkImage, cutter, boTimeStep, dummy);
}
template <typename TPixel, unsigned int VImageDimension>
void CutImage(itk::VectorImage<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep)
{
TPixel *dummy = nullptr;
CutImageWithOutputTypeSelect<TPixel, VImageDimension, TPixel>(inputItkImage, cutter, boTimeStep, dummy);
}
} // of namespace mitk
#include "mitkImageCast.h"
#endif // of MITKBOUNDINGOBJECTCUTTER_TXX
diff --git a/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h b/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h
index 290bc93c66..d7c22530b9 100644
--- a/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h
+++ b/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h
@@ -1,118 +1,118 @@
/*============================================================================
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 mitkCovarianceMatrixCalculator_h
#define mitkCovarianceMatrixCalculator_h
// exports
#include "MitkAlgorithmsExtExports.h"
#include <mitkCommon.h>
#include <itkMatrix.h>
#include <itkObjectFactory.h>
#include <vector>
namespace mitk
{
// forward declarations
class Surface;
struct CovarianceMatrixCalculatorData;
/**
* \ingroup AnisotropicRegistration
*
* @brief Class that computes the covariance matrices for every point in a
* {@link Surface} used in the A-ICP algorithm.
*
* Computes a covariance matrix for every vertex in a given {@link Surface}
* based on it's direct neighbours and saves them into a CovarianceMatrixList.
* The Class implements the CM_PCA method presented by
* L. Maier-Hein et al. in "Convergent Iterative Closest-Point Algorithm
- * to Accomodate Anisotropic and Inhomogenous Localization Error.",
+ * to Accommodate Anisotropic and Inhomogenous Localization Error.",
* IEEE T Pattern Anal 34 (8), 1520-1532, 2012. The algorithm needs
* a clean Surface with non manifold edges and no duplicated vertices. To
* ensure a clean Surface representation use vtkCleanPolyData.
*/
class MITKALGORITHMSEXT_EXPORT CovarianceMatrixCalculator : public itk::Object
{
private:
/** Pimpl to hold private data.*/
CovarianceMatrixCalculatorData *d;
protected:
// local typedefs
/** Definition of the covariance matrix.*/
typedef itk::Matrix<double, 3, 3> CovarianceMatrix;
/** Definition of a list of covariance matrices */
typedef std::vector<CovarianceMatrix> CovarianceMatrixList;
typedef double Vertex[3];
/** List that stores the computed covariance matrices. */
CovarianceMatrixList m_CovarianceMatrixList;
/** This method projects all surrounding vertices of given vertex in a Surface
* in the normal direction onto a plane and computes a primary component
* analysis on the projected vertices. In the next step a orthonormal
* system is created.
*
* @param index The index of the input Vertex in the Surface.
* @param normal The normal of the input Vertex.
* @param principalComponents CovarianceMatrix of the principal component analysis.
* @param variances Variances along the axes of the createt Orthonormal system.
* @param curVertex The current Vertex in the surface
*
*/
void ComputeOrthonormalCoordinateSystem(
const int index, Vertex normal, CovarianceMatrix &principalComponents, Vertex variances, Vertex curVertex);
CovarianceMatrixCalculator();
~CovarianceMatrixCalculator() override;
public:
mitkClassMacroItkParent(CovarianceMatrixCalculator, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Sets the scaling factor for the voronoi area.
* @param factor The scaling factor.
*/
void SetVoronoiScalingFator(const double factor);
/** Enables/disables the covariance matrix normalization.
* @param state Enables the covariance matrix normalization.
*/
void EnableNormalization(bool state);
/** Returns the mean of variance of all computed covariance matrices.
* @return The mean variance.
*/
double GetMeanVariance() const;
/** Returns a reference to the CovarianceMatrixList with the computed covariance matrices.
* @return A CovarianceMatrixList.
*/
const CovarianceMatrixList &GetCovarianceMatrices() const;
/** Sets the input {@link Surface} for which the covariance matrices will be calculated.
* @param input A {@link Surface}.
*/
void SetInputSurface(Surface *input);
/** Method that computes the covariance matrices for the input surface.
* @throws std::exception If the input surface is not set.
*/
void ComputeCovarianceMatrices();
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h b/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h
index 647327c440..6ddc54e2fe 100644
--- a/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h
@@ -1,182 +1,182 @@
/*============================================================================
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 mitkGeometryClipImageFilter_h
#define mitkGeometryClipImageFilter_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkCommon.h"
#include "mitkGeometryData.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
namespace itk
{
template <class TPixel, unsigned int VImageDimension>
class ITK_EXPORT Image;
}
namespace mitk
{
//##Documentation
//## @brief Filter for clipping an image with a PlaneGeometry
//##
//## The given geometry for clipping can be either a PlaneGeometry
//## or a TimeGeometry containing multiple instances
//## of PlaneGeometry
//##
//## \todo add AutoOrientLabels, which makes the "left" side (minimum X value) side of the image get one defined
//label.
//## left-most because vtkPolyDataNormals uses the same definition and this filter is used for visualization of
//## front/back side of curved planes
//##
//## @ingroup Process
class MITKALGORITHMSEXT_EXPORT GeometryClipImageFilter : public ImageToImageFilter
{
public:
mitkClassMacro(GeometryClipImageFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* Set the geometry to be used for clipping
*
* The given geometry for clipping must be a PlaneGeometry.
*/
void SetClippingGeometry(const mitk::BaseGeometry *aClippingGeometry);
/**
* Set the geometry to be used for clipping
*
* The given geometry for clipping must a
* TimeGeometry containing multiple instances
* of PlaneGeometry
*/
void SetClippingGeometry(const mitk::TimeGeometry *aClippingGeometry);
const mitk::BaseGeometry *GetClippingGeometry() const;
const mitk::TimeGeometry *GetClippingTimeGeometry() const;
//##Description
//## @brief Get whether the part above or below the geometry
//## shall be clipped (default: @a true)
itkGetConstMacro(ClipPartAboveGeometry, bool);
//## @brief Set whether the part above or below the geometry
//## shall be clipped (default: @a true)
itkSetMacro(ClipPartAboveGeometry, bool);
//## @brief Set whether the part above or below the geometry
//## shall be clipped (default: @a true)
itkBooleanMacro(ClipPartAboveGeometry);
//##Description
//## @brief Set value for outside pixels (default: 0),
//## used when m_AutoOutsideValue is \a false
itkSetMacro(OutsideValue, ScalarType);
itkGetConstMacro(OutsideValue, ScalarType);
//##Description
- //## @brief If set to \a true the minimum of the ouput pixel type is
+ //## @brief If set to \a true the minimum of the output pixel type is
//## used as outside value (default: \a false)
itkSetMacro(AutoOutsideValue, bool);
itkGetConstMacro(AutoOutsideValue, bool);
itkBooleanMacro(AutoOutsideValue);
itkSetMacro(AutoOrientLabels, bool);
itkGetConstMacro(AutoOrientLabels, bool);
//##Description
//## @brief If set to \a true both sides of the clipping
- //## geometry will be labeld using m_AboveGeometryLabel and
+ //## geometry will be labeled using m_AboveGeometryLabel and
//## m_BelowGeometryLabel
itkSetMacro(LabelBothSides, bool);
itkGetConstMacro(LabelBothSides, bool);
itkBooleanMacro(LabelBothSides);
//##Description
//## @brief Set for voxels above the clipping geometry.
//## This value is only used, if m_LabelBothSides is set to true.
itkSetMacro(AboveGeometryLabel, ScalarType);
itkGetConstMacro(AboveGeometryLabel, ScalarType);
//##Description
//## @brief Set for voxels below the clipping geometry.
//## This value is only used, if m_LabelBothSides is set to true.
itkSetMacro(BelowGeometryLabel, ScalarType);
itkGetConstMacro(BelowGeometryLabel, ScalarType);
protected:
GeometryClipImageFilter();
~GeometryClipImageFilter() override;
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
template <typename TPixel, unsigned int VImageDimension>
void _InternalComputeClippedImage(itk::Image<TPixel, VImageDimension> *itkImage,
mitk::GeometryClipImageFilter *geometryClipper,
const mitk::PlaneGeometry *clippingPlaneGeometry);
mitk::BaseGeometry::ConstPointer m_ClippingGeometry;
mitk::GeometryData::Pointer m_ClippingGeometryData;
mitk::TimeGeometry::ConstPointer m_TimeClippingGeometry;
mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
//##Description
//## @brief Defines whether the part above or below the geometry
//## shall be clipped (default: @a true)
bool m_ClipPartAboveGeometry;
//##Description
//## @brief Value for outside pixels (default: 0)
//##
//## Used only if m_AutoOutsideValue is \a false.
ScalarType m_OutsideValue;
//##Description
- //## @brief If \a true the minimum of the ouput pixel type is
+ //## @brief If \a true the minimum of the output pixel type is
//## used as outside value (default: \a false)
bool m_AutoOutsideValue;
//##Description
//## @brief If \a true all pixels above and below the geometry
//## are labeled with m_AboveGeometryLabel and m_BelowGeometryLabel
bool m_LabelBothSides;
/**
* \brief Orient above like vtkPolyDataNormals does with AutoOrientNormals
*/
bool m_AutoOrientLabels;
//##Description
//## @brief Is used for labeling all pixels above the geometry
//## when m_LabelBothSides is on
ScalarType m_AboveGeometryLabel;
//##Description
//## @brief Is used for labeling all pixels below the geometry
//## when m_LabelBothSides is on
ScalarType m_BelowGeometryLabel;
//##Description
//## @brief Time when Header was last initialized
itk::TimeStamp m_TimeOfHeaderInitialization;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h b/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h
index 5a04d3436a..0a60edf166 100644
--- a/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h
+++ b/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h
@@ -1,68 +1,68 @@
/*============================================================================
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 mitkGeometryDataSource_h
#define mitkGeometryDataSource_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkBaseDataSource.h"
namespace mitk
{
class GeometryData;
/**
* @brief Superclass of all classes generating GeometryData (instances of class
* GeometryData) 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::GeometryDataSource.
* @ingroup Process
*/
class MITKALGORITHMSEXT_EXPORT GeometryDataSource : public BaseDataSource
{
public:
mitkClassMacro(GeometryDataSource, BaseDataSource);
itkNewMacro(Self);
typedef mitk::GeometryData OutputType;
mitkBaseDataSourceGetOutputDeclarations
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
GeometryDataSource();
~GeometryDataSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h b/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h
index ad8d0db096..b75e97dd45 100644
--- a/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h
@@ -1,100 +1,100 @@
/*============================================================================
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 mitkImageToUnstructuredGridFilter_h
#define mitkImageToUnstructuredGridFilter_h
#include <MitkAlgorithmsExtExports.h>
#include <mitkCommon.h>
#include <mitkImage.h>
#include <mitkUnstructuredGrid.h>
#include <mitkUnstructuredGridSource.h>
namespace mitk
{
/**
* @brief Converts an Image into an UnstructuredGrid represented by Points.
* The filter uses a Threshold to extract every pixel, with value higher than
* the threshold, as point.
* If no threshold is set, every pixel is extracted as a point.
*/
class MITKALGORITHMSEXT_EXPORT ImageToUnstructuredGridFilter : public UnstructuredGridSource
{
public:
mitkClassMacro(ImageToUnstructuredGridFilter, UnstructuredGridSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** This method is called by Update(). */
void GenerateData() override;
/** Initializes the output information */
void GenerateOutputInformation() override;
/** Returns a const reference to the input image */
const mitk::Image *GetInput(void) const;
mitk::Image *GetInput(void);
/** Set the source image. As input every mitk 3D image can be used. */
using itk::ProcessObject::SetInput;
virtual void SetInput(const mitk::Image *image);
/**
* Set the threshold for extracting points. Every pixel, which value
* is higher than this value, will be a point.
*/
void SetThreshold(double threshold);
/** Returns the threshold */
double GetThreshold();
/** Returns the number of extracted points after edge detection */
itkGetMacro(NumberOfExtractedPoints, int);
protected :
/** Constructor */
ImageToUnstructuredGridFilter();
/** Destructor */
~ImageToUnstructuredGridFilter() override;
/**
* Access method for extracting the points from the input image
*/
template <typename TPixel, unsigned int VImageDimension>
void ExtractPoints(const itk::Image<TPixel, VImageDimension> *image);
/** The number of points extracted by the filter */
int m_NumberOfExtractedPoints;
private:
/**
- * Geometry of the input image, needed to tranform the image points
+ * Geometry of the input image, needed to transform the image points
* into world points
*/
mitk::BaseGeometry *m_Geometry;
/** Threshold for extracting the points */
double m_Threshold;
/** The output of the filter, which contains the extracted points */
mitk::UnstructuredGrid::Pointer m_UnstructGrid;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkPlaneFit.h b/Modules/AlgorithmsExt/include/mitkPlaneFit.h
index ea215b16b6..7601dd7c0d 100644
--- a/Modules/AlgorithmsExt/include/mitkPlaneFit.h
+++ b/Modules/AlgorithmsExt/include/mitkPlaneFit.h
@@ -1,138 +1,138 @@
/*============================================================================
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 mitkPlaneFit_h
#define mitkPlaneFit_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkGeometryDataSource.h"
#include "mitkPlaneGeometry.h"
#include "mitkPointSet.h"
#include "mitkTimeGeometry.h"
namespace mitk
{
//!
// kind regards to dr. math!
// function [x0, a, d, normd] = lsplane(X)
// ---------------------------------------------------------------------
// LSPLANE.M Least-squares plane (orthogonal distance
// regression).
//
// Version 1.0
// Last amended I M Smith 27 May 2002.
// Created I M Smith 08 Mar 2002
// ---------------------------------------------------------------------
// Input
// X Array [x y z] where x = vector of x-coordinates,
// y = vector of y-coordinates and z = vector of
// z-coordinates.
// Dimension: m x 3.
//
// Output
// x0 Centroid of the data = point on the best-fit plane.
// Dimension: 3 x 1.
//
// a Direction cosines of the normal to the best-fit
// plane.
// Dimension: 3 x 1.
//
// <Optional...
// d Residuals.
// Dimension: m x 1.
//
// normd Norm of residual errors.
// Dimension: 1 x 1.
// ...>
//
// [x0, a <, d, normd >] = lsplane(X)
// ---------------------------------------------------------------------
class MITKALGORITHMSEXT_EXPORT PlaneFit : public GeometryDataSource
{
public:
mitkClassMacro(PlaneFit, GeometryDataSource);
itkNewMacro(Self);
typedef mitk::PointSet::PointDataType PointDataType;
typedef mitk::PointSet::PointDataIterator PointDataIterator;
void GenerateOutputInformation() override;
void GenerateData() override;
/*!Getter for point set.
*
*/
const mitk::PointSet *GetInput();
/*! filter initialisation.
*
*/
using mitk::GeometryDataSource::SetInput;
virtual void SetInput(const mitk::PointSet *ps);
/*! returns the center of gravity of the point set.
*
*/
virtual const mitk::Point3D &GetCentroid(int t = 0) const;
/*! returns the plane geometry which represents the point set.
*
*/
virtual mitk::PlaneGeometry::Pointer GetPlaneGeometry(int t = 0);
/*! returns the normal of the plane which represents the point set.
*
*/
virtual const mitk::Vector3D &GetPlaneNormal(int t = 0) const;
protected:
PlaneFit();
~PlaneFit() override;
/*! Calculates the centroid of the point set.
* the center of gravity is calculated through the mean value of the whole point set
*/
void CalculateCentroid(int t = 0);
/*! working with an SVD algorithm form matrix dataM.
- * ITK suplies the vnl_svd to solve an plan fit eigentvector problem
+ * ITK supplies the vnl_svd to solve an plan fit eigentvector problem
* points are processed in the SVD matrix. The normal vector is the
* singular vector of dataM corresponding to its smalest singular value.
- * The mehtod uses VNL library from ITK and at least the mehtod nullvector()
+ * The method uses VNL library from ITK and at least the method nullvector()
* to extract the normalvector.
*/
void ProcessPointSet(int t = 0);
/*! Initialize Plane and configuration.
*
*/
void InitializePlane(int t = 0);
private:
/*!keeps a copy of the pointset.*/
const mitk::PointSet *m_PointSet;
/* output object - a time sliced geometry.*/
mitk::TimeGeometry::Pointer m_TimeGeometry;
std::vector<mitk::PlaneGeometry::Pointer> m_Planes;
/*! the calculatet center point of all points in the point set.*/
std::vector<mitk::Point3D> m_Centroids;
- /* the normal vector to descrie a plane gemoetry.*/
+ /* the normal vector to describe a plane geometry.*/
std::vector<mitk::Vector3D> m_PlaneVectors;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h b/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h
index 9101aeb147..27a7116957 100644
--- a/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h
@@ -1,55 +1,55 @@
/*============================================================================
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 mitkSurfaceToPointSetFilter_h
#define mitkSurfaceToPointSetFilter_h
// exports
#include "MitkAlgorithmsExtExports.h"
// mitk headers
#include "mitkSurface.h"
#include <mitkPointSetSource.h>
//#include <itkExtendedDoublyLinkedFaceList.h>
namespace mitk
{
/** Documentation
* @brief This filter converts the input surface into a point set. The output point set contains every point exactly
* one time
- * (no dublicated points like in the stl-format).
+ * (no duplicated points like in the stl-format).
*/
class MITKALGORITHMSEXT_EXPORT SurfaceToPointSetFilter : public mitk::PointSetSource
{
public:
mitkClassMacro(SurfaceToPointSetFilter, mitk::PointSetSource);
itkNewMacro(Self);
using itk::ProcessObject::SetInput;
void SetInput(mitk::Surface::Pointer m_InputSurface);
std::string GetErrorMessage();
protected:
SurfaceToPointSetFilter();
~SurfaceToPointSetFilter() override;
/** @brief method generating the output of this filter. Called in the updated process of the pipeline. */
void GenerateData() override;
//############### members ########################
mitk::Surface::Pointer m_InputSurface;
std::string m_ErrorMessage;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h b/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h
index d22c020acd..a8bf1def62 100644
--- a/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h
@@ -1,128 +1,128 @@
/*============================================================================
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 mitkUnstructuredGridClusteringFilter_h
#define mitkUnstructuredGridClusteringFilter_h
#include <MitkAlgorithmsExtExports.h>
#include <mitkCommon.h>
#include <mitkUnstructuredGrid.h>
#include <mitkUnstructuredGridToUnstructuredGridFilter.h>
#include <vtkIdList.h>
#include <vtkPoints.h>
#include <vtkSmartPointer.h>
namespace mitk
{
/**
* @brief This filter uses the DBSCAN algorithm for clustering an
* mitk::UnstructuredGrid. "MinPts" defines the number of neighbours which are
* required to be a kernel point if a point is in range of a kernel point
- * but hasnt enough neighbours this point is added to the cluster but is a
+ * but hasn't enough neighbours this point is added to the cluster but is a
* density reachable point and the cluster ends at this point. "eps" is the
* range in which the neighbours are searched. If "Meshing" is set the
* clusteres UnstructuredGrid is meshed and visible in 2D renderwindows.
*
* DBSCAN algorithm:
*
* DBSCAN(D, eps, MinPts)
* C = 0
* for each unvisited point P in dataset D
* mark P as visited
* N = D.regionQuery(P, eps)
* if sizeof(N) < MinPts
* mark P as NOISE
* else
* C = next cluster
* expandCluster(P, N, C, eps, MinPts)
*
* expandCluster(P, N, C, eps, MinPts)
* add P to cluster C
* for each point P' in N
* if P' is not visited
* mark P' as visited
* N' = D.regionQuery(P', eps)
* if sizeof(N') >= MinPts
* N = N joined with N'
* if P' is not yet member of any cluster
* add P' to cluster C
*/
class MITKALGORITHMSEXT_EXPORT UnstructuredGridClusteringFilter : public UnstructuredGridToUnstructuredGridFilter
{
public:
mitkClassMacro(UnstructuredGridClusteringFilter, UnstructuredGridToUnstructuredGridFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Sets the distance for the neighbour search */
itkSetMacro(eps, double);
itkGetMacro(eps, double);
/** Sets the number of required neighbours */
itkSetMacro(MinPts, int);
itkGetMacro(MinPts, int);
/** If activated the clusteres UnstructuredGrid is meshed */
itkSetMacro(Meshing, bool);
/** Returns all clusters as UnstructuredGrids which were found */
virtual std::vector<mitk::UnstructuredGrid::Pointer> GetAllClusters();
/** Returns the number of the clusters which were found */
virtual int GetNumberOfFoundClusters();
protected:
/** Constructor */
UnstructuredGridClusteringFilter();
/** Destructor */
~UnstructuredGridClusteringFilter() override;
/** Defines the output of the filter */
void GenerateOutputInformation() override;
/** Is called by the Update() method */
void GenerateData() override;
private:
/** Used for the DBSCAN algorithm to expand a cluster and add more points to it */
void ExpandCluster(int id, vtkIdList *pointIDs, vtkPoints *cluster, vtkPoints *inpPoints);
/** The result main Cluster */
mitk::UnstructuredGrid::Pointer m_UnstructGrid;
/** All clusters which were found */
std::vector<vtkSmartPointer<vtkPoints>> m_Clusters;
/** The distances of the points from the input UnstructuredGrid*/
std::vector<vtkSmartPointer<vtkDoubleArray>> m_DistanceArrays;
- /** The range for the neighbout search */
+ /** The range for the neighbour search */
double m_eps;
/** The number of the required neighbours */
int m_MinPts;
/** Activates the meshing for the UnstructuredGrid clusters*/
bool m_Meshing;
/** If its activated the distance of the clusters is used instead of the
* size */
bool m_DistCalc;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h b/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h
index 670d873266..dc1b53189c 100644
--- a/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h
+++ b/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h
@@ -1,247 +1,247 @@
/*============================================================================
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 mitkWeightedPointTransform_h
#define mitkWeightedPointTransform_h
// EXPORTS
#include "MitkAlgorithmsExtExports.h"
// ITK
#include <itkMatrix.h>
#include <itkVariableSizeMatrix.h>
#include <mitkCommon.h>
#include <mitkPointSet.h>
#include <vector>
#include <vtkSmartPointer.h>
// forward declarations
class vtkPoints;
class vtkLandmarkTransform;
namespace mitk
{
/**
* \ingroup AnisotropicRegistration
*
* @brief This class implements an extension of the
* weighted point based registration algorithm
* from A. Danilchenko, R. Balachandran and J. M. Fitzpatrick.
*
* The class implements an extension of the weighted point based registration
* from A. Danilchenko et al.
* presented by L. Maier-Hein et al. in "Convergent Iterative Closest-Point Algorithm
- * to Accomodate Anisotropic and Inhomogenous Localization Error.",
+ * to Accommodate Anisotropic and Inhomogenous Localization Error.",
* IEEE T Pattern Anal 34 (8), 1520-1532, 2012. The extension computes, in order
* to ensure the convergence of the algorithm, an isotropic estimation
* by an unweighted point based registration algorithm as an initial estimate.
* The implemantion was originally ported to C/C++ by A. Franz.
*
* \note Some methods are accelerated when OpenMP is enabled.
*
*/
class MITKALGORITHMSEXT_EXPORT WeightedPointTransform : public itk::Object
{
/** Definition of a 3x3 matrix.*/
typedef itk::Matrix<double, 3, 3> Matrix3x3;
/** Definition of a 3x3 Weighting matrix.*/
typedef Matrix3x3 WeightMatrix;
/** Definition of a Rotation matrix.*/
typedef Matrix3x3 Rotation;
/** Definition of a translation vector.*/
typedef itk::Vector<double, 3> Translation;
/** Definition of a weight matrix list.*/
typedef std::vector<WeightMatrix> WeightMatrixList;
/** Definition of a covariance matrix list.*/
typedef std::vector<Matrix3x3> CovarianceMatrixList;
public:
mitkClassMacroItkParent(WeightedPointTransform, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** @brief Method which registers both point sets. */
void ComputeTransformation();
/** @brief Sets the threshold of the registration. Default value is 0.0001.*/
itkSetMacro(Threshold, double);
/** @brief Sets the maximum number of iterations of the registration.
* Default value is 1000.
*/
itkSetMacro(MaxIterations, double);
/** @return Returns the number of iterations of the last run
* of the registration algorithm. Returns -1 if there was no
* run of the registration yet.
*/
itkGetMacro(Iterations, int);
/** @return Returns the FRE of the last run of the registration algorithm.
* Returns -1 if there was no run of the registration yet.
*/
itkGetMacro(FRE, double);
/** @brief Sets the FRE normalization factor. Default value is 1.0. */
itkSetMacro(FRENormalizationFactor, double);
/** @return Returns the current FRE normalization factor.*/
itkGetMacro(FRENormalizationFactor, double);
/** Sets the moving point set used for the registration.
* @param p The input point set.
*/
void SetMovingPointSet(vtkSmartPointer<vtkPoints> p);
/**
* Set the list of 3x3 covariance matrices belonging to the moving point set.
* @param matrices List of covariance matrices.
*/
void SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices);
/** Sets the fixed point set used for the registration.
* @param p The input point set.
*/
void SetFixedPointSet(vtkSmartPointer<vtkPoints> p);
/**
* Set the list of 3x3 covariance matrices belonging to the fixed point set.
* @param matrices List of covariance matrices.
*/
void SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices);
/**
* The translation vector computed by the algorithm.
* @return 3x1 translation vector.
*/
const Translation &GetTransformT() const { return m_Translation; }
/**
* The rotation matrix computed by the algorithm.
*/
const Rotation &GetTransformR() const { return m_Rotation; }
protected:
WeightedPointTransform();
~WeightedPointTransform() override;
/** Threshold used to terminate the algorithm.*/
double m_Threshold;
/** Max allowed iterations used by the algorithm.*/
int m_MaxIterations;
/** The amount of iterations needed by the algorithm.*/
int m_Iterations;
/** The fiducial registration error (FRE) used in the algorithm.*/
double m_FRE;
/** Normalization factor for the FRE.*/
double m_FRENormalizationFactor;
/** Isotropic point based registration used for initial estimate.*/
vtkSmartPointer<vtkLandmarkTransform> m_LandmarkTransform;
/** The fixed point set (Y).*/
vtkSmartPointer<vtkPoints> m_FixedPointSet;
/** Moving point set (X).*/
vtkSmartPointer<vtkPoints> m_MovingPointSet;
/** Covariance matrices of the moving point set (Sigma_X).*/
CovarianceMatrixList m_CovarianceMatricesMoving;
/** Covariance matrices of the moving point set (Sigma_Y).*/
CovarianceMatrixList m_CovarianceMatricesFixed;
/** 3x1 translation vector.*/
Translation m_Translation;
/** 3x3 rotation matrix.*/
Rotation m_Rotation;
/**
* original matlab-function:
*
* Constructs the C matrix of the linear version of the registration
* problem, Cq = e, where q = [delta_angle(1:3),delta_translation(1:3)] and
* e is produced by e_maker(X,Y,W)
*
* Authors: JM Fitzpatrick and R Balachandran
* Creation: February 2009
*
* --------------------------------------------
*
* converted to C++ by Alfred Franz in March/April 2010
*/
void C_maker(vtkPoints *X, const WeightMatrixList &W, itk::VariableSizeMatrix<double> &returnValue);
/**
* original matlab-function:
*
* Constructs the e vector of the linear version of the registration
* problem, Cq = e, where q = [delta_angle(1:3),delta_translation(1:3)] and
* C is produced by C_maker(X,W)
*
* Authors: JM Fitzpatrick and R Balachandran
* Creation: February 2009
*
* --------------------------------------------
*
* converted to C++ by Alfred Franz in March/April 2010
*/
void E_maker(vtkPoints *X, vtkPoints *Y, const WeightMatrixList &W, vnl_vector<double> &returnValue);
/**
* This method computes the change in a root mean squared
* sense between the previous and the actual iteration.
* The computed value is used as a termination constraint of the algorithm and
* compared against the threshold.
*
* @param X The moving point set in the previous iteration step.
* @param X_new The moving point set in the actual step.
*
* @return The computed change between the two point sets.
*/
double CalculateConfigChange(vtkPoints *X, vtkPoints *X_new);
/**
* @brief This method performs a variant of the weighted point register algorithm presented by
* A. Danilchenko, R. Balachandran and J. M. Fitzpatrick in January 2010. (Modified in January 2011)
* converted to C++ by Alfred Franz in March/April 2010
*
* @param X (input) the moving point set
* @param Y (input) the fixed (static) point set
* @param Sigma_X (input) a 3-by-3-by-N array, each page containing the weighting matrix for the Nth pair
* of points in X
* @param Sigma_Y (input) a 3-by-3-by-N array, each page containing the weighting matrix for the Nth pair
* of points in Y
* @param Threshold (input) the relative size of the change to the moving set above which the iteration
* continues
* @param MaxIterations (input) the maximum number of iterations allowed
* @param TransformationR (output) this variable will hold the computed rotation matrix
* @param TransformationT (output) this variable will hold the computed translation vector
* @param FRE (output) this variable will hold the computed rotation FRE of the transformation
* @param n (output) this variable will hold the number of iterations used by the algorithm
*/
void WeightedPointRegister(vtkPoints *X,
vtkPoints *Y,
const CovarianceMatrixList &Sigma_X,
const CovarianceMatrixList &Sigma_Y,
double Threshold,
int MaxIterations,
Rotation &TransformationR,
Translation &TransformationT,
double &FRE,
int &n);
};
}
#endif
diff --git a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
index e34ff3e2c3..729732e83d 100644
--- a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
+++ b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
@@ -1,412 +1,412 @@
/*============================================================================
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 "mitkCovarianceMatrixCalculator.h"
#include <mitkExceptionMacro.h>
#include <mitkSurface.h>
#include <vtkCell.h>
#include <vtkCellLinks.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkPolyDataNormals.h>
#include <vtkSmartPointer.h>
// forward declarations of private functions
static vtkIdList *GetNeighboursOfPoint(unsigned int index, vtkPolyData *polydata);
static vtkIdList *CalculatePCAonPointNeighboursForNormalVector(int index,
double normal[3],
itk::Matrix<double, 3, 3> &mat,
double curVertex[3],
std::vector<mitk::Point3D> &pointList,
vtkPolyData *polyData);
static itk::Matrix<double, 3, 3> ComputeCovarianceMatrix(itk::Matrix<double, 3, 3> &axes,
double sigma[3],
double normalizationValue);
namespace mitk
{
/** \brief Pimpl to hold the private data in the CovarianceMatrixCalculator.*/
struct CovarianceMatrixCalculatorData
{
vtkPolyDataNormals *m_PolyDataNormals;
vtkPolyData *m_PolyData;
Surface *m_Input;
double m_VoronoiScalingFactor;
bool m_EnableNormalization;
double m_MeanVariance;
CovarianceMatrixCalculatorData()
: m_PolyDataNormals(vtkPolyDataNormals::New()),
m_PolyData(nullptr),
m_Input(nullptr),
m_VoronoiScalingFactor(1.0),
m_EnableNormalization(false),
m_MeanVariance(0.0)
{
m_PolyDataNormals->SplittingOff();
}
~CovarianceMatrixCalculatorData()
{
if (m_PolyDataNormals)
m_PolyDataNormals->Delete();
}
};
}
mitk::CovarianceMatrixCalculator::CovarianceMatrixCalculator() : d(new CovarianceMatrixCalculatorData())
{
}
mitk::CovarianceMatrixCalculator::~CovarianceMatrixCalculator()
{
delete d;
}
void mitk::CovarianceMatrixCalculator::SetVoronoiScalingFator(const double factor)
{
d->m_VoronoiScalingFactor = factor;
}
void mitk::CovarianceMatrixCalculator::EnableNormalization(bool state)
{
d->m_EnableNormalization = state;
}
double mitk::CovarianceMatrixCalculator::GetMeanVariance() const
{
return d->m_MeanVariance;
}
const mitk::CovarianceMatrixCalculator::CovarianceMatrixList &mitk::CovarianceMatrixCalculator::GetCovarianceMatrices()
const
{
return m_CovarianceMatrixList;
}
void mitk::CovarianceMatrixCalculator::SetInputSurface(Surface *input)
{
d->m_Input = input;
}
void mitk::CovarianceMatrixCalculator::ComputeCovarianceMatrices()
{
double normalizationValue = -1.0;
vtkDataArray *normals = nullptr;
d->m_MeanVariance = 0.0;
if (!d->m_Input)
mitkThrow() << "No input surface was set in mitk::CovarianceMatrixCalculator";
d->m_PolyData = d->m_Input->GetVtkPolyData();
// Optional normal calculation can be disabled to use the normals
// of the surface:
// normals = d->m_PolyData->GetPointData()->GetNormals();
//// compute surface normals if the surface has no normals
// if ( normals == nullptr )
//{
d->m_PolyDataNormals->SetInputData(d->m_PolyData);
d->m_PolyDataNormals->Update();
normals = d->m_PolyDataNormals->GetOutput()->GetPointData()->GetNormals();
//}
if (d->m_EnableNormalization)
normalizationValue = 1.5;
// clear the matrixlist
m_CovarianceMatrixList.clear();
// allocate memory if required
if (d->m_PolyData->GetNumberOfPoints() > (vtkIdType)m_CovarianceMatrixList.capacity())
m_CovarianceMatrixList.reserve(d->m_PolyData->GetNumberOfPoints());
for (vtkIdType i = 0; i < d->m_PolyData->GetNumberOfPoints(); ++i)
{
Vertex normal;
Vertex currentVertex;
Vertex variances = {0.0, 0.0, 0.0};
CovarianceMatrix mat;
mat.Fill(0.0);
normals->GetTuple(i, normal);
d->m_PolyData->GetPoint(i, currentVertex);
ComputeOrthonormalCoordinateSystem(i, normal, mat, variances, currentVertex);
// use prefactor for sigma along surface
variances[0] = (d->m_VoronoiScalingFactor * variances[0]);
variances[1] = (d->m_VoronoiScalingFactor * variances[1]);
variances[2] = (d->m_VoronoiScalingFactor * variances[2]);
d->m_MeanVariance += (variances[0] + variances[1] + variances[2]);
// compute the covariance matrix and save it
const CovarianceMatrix covarianceMatrix = ComputeCovarianceMatrix(mat, variances, normalizationValue);
m_CovarianceMatrixList.push_back(covarianceMatrix);
}
if (d->m_EnableNormalization)
d->m_MeanVariance = normalizationValue / 3.0;
else
d->m_MeanVariance /= (3.0 * (double)d->m_PolyData->GetNumberOfPoints());
// reset input
d->m_PolyData = nullptr;
d->m_Input = nullptr;
}
-// Get a list with the id's of all surrounding conected vertices
+// Get a list with the id's of all surrounding connected vertices
// to the current vertex at the given index in the polydata
vtkIdList *GetNeighboursOfPoint(unsigned int index, vtkPolyData *polydata)
{
vtkIdList *cellIds = vtkIdList::New();
vtkIdList *result = vtkIdList::New();
polydata->GetPointCells(index, cellIds);
for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); j++)
{
vtkIdList *newPoints = polydata->GetCell(cellIds->GetId(j))->GetPointIds();
for (vtkIdType k = 0; k < newPoints->GetNumberOfIds(); k++)
{
// if point has not yet been inserted add id
if (result->IsId(newPoints->GetId(k)) == -1)
{
result->InsertNextId(newPoints->GetId(k));
}
}
}
cellIds->Delete();
return result;
}
-// Computes a primary component analysis of the surounding vertices
+// Computes a primary component analysis of the surrounding vertices
// of the verex at the current index.
vtkIdList *CalculatePCAonPointNeighboursForNormalVector(int index,
double normal[3],
itk::Matrix<double, 3, 3> &mat,
double curVertex[3],
std::vector<mitk::Point3D> &pointList,
vtkPolyData *polyData)
{
typedef std::vector<mitk::Point3D> VectorType;
typedef VectorType::const_iterator ConstPointIterator;
typedef double Vertex[3];
Vertex mean = {0.0, 0.0, 0.0};
Vertex tmp = {0.0, 0.0, 0.0};
vtkIdList *neighbourPoints = GetNeighboursOfPoint(index, polyData);
const vtkIdType size = neighbourPoints->GetNumberOfIds();
// reserve memory for all neighbours
pointList.reserve(size);
// project neighbours on plane given by normal
// and compute mean
for (vtkIdType i = 0; i < size; ++i)
{
mitk::Point3D p;
Vertex resultPoint;
polyData->GetPoint((neighbourPoints->GetId(i)), tmp);
vtkPlane::GeneralizedProjectPoint(tmp, curVertex, normal, resultPoint);
p[0] = resultPoint[0];
p[1] = resultPoint[1];
p[2] = resultPoint[2];
mean[0] += p[0];
mean[1] += p[1];
mean[2] += p[2];
pointList.push_back(p);
}
mean[0] /= (double)size;
mean[1] /= (double)size;
mean[2] /= (double)size;
// compute the covariances with matrix multiplication
for (ConstPointIterator it = pointList.begin(); it != pointList.end(); ++it)
{
tmp[0] = ((*it)[0] - mean[0]);
tmp[1] = ((*it)[1] - mean[1]);
tmp[2] = ((*it)[2] - mean[2]);
// on diagonal elements
mat[0][0] += tmp[0] * tmp[0];
mat[1][1] += tmp[1] * tmp[1];
mat[2][2] += tmp[2] * tmp[2];
// of diagonal elements
mat[1][0] += tmp[0] * tmp[1];
mat[2][0] += tmp[0] * tmp[2];
mat[2][1] += tmp[1] * tmp[2];
}
// copy upper triangle to lower triangle,
- // we got a symetric matrix
+ // we got a symmetric matrix
mat[0][1] = mat[1][0];
mat[0][2] = mat[2][0];
mat[1][2] = mat[2][1];
// variance
mat /= (size - 1);
vnl_svd<double> svd(mat.GetVnlMatrix().as_ref());
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
mat[i][j] = svd.U()[j][i];
return neighbourPoints;
}
// Computes an orthonormal system for a vertex with it's surrounding neighbours.
void mitk::CovarianceMatrixCalculator::ComputeOrthonormalCoordinateSystem(
const int index, Vertex normal, CovarianceMatrix &axes, Vertex variances, Vertex curVertex)
{
typedef std::vector<mitk::Point3D> VectorType;
typedef VectorType::const_iterator ConstPointIterator;
VectorType projectedPoints;
Vertex meanValues = {0.0, 0.0, 0.0};
// project neighbours to new coordinate system and get principal axes
vtkIdList *neighbourPoints =
CalculatePCAonPointNeighboursForNormalVector(index, normal, axes, curVertex, projectedPoints, d->m_PolyData);
// Set the normal as the third principal axis
axes[2][0] = normal[0];
axes[2][1] = normal[1];
axes[2][2] = normal[2];
for (vtkIdType i = 0; i < neighbourPoints->GetNumberOfIds(); ++i)
{
mitk::Point3D projectedPoint;
Vertex curNeighbour;
d->m_PolyData->GetPoint(neighbourPoints->GetId(i), curNeighbour);
curNeighbour[0] = curNeighbour[0] - curVertex[0];
curNeighbour[1] = curNeighbour[1] - curVertex[1];
curNeighbour[2] = curNeighbour[2] - curVertex[2];
for (int k = 0; k < 3; ++k)
{
projectedPoint[k] = axes[k][0] * curNeighbour[0] + axes[k][1] * curNeighbour[1] + axes[k][2] * curNeighbour[2];
meanValues[k] += projectedPoint[k];
}
// reuse the allocated vector from the PCA on the point neighbours
projectedPoints[i] = projectedPoint;
}
meanValues[0] /= (double)projectedPoints.size();
meanValues[1] /= (double)projectedPoints.size();
meanValues[2] /= (double)projectedPoints.size();
// compute variances along new axes
for (ConstPointIterator it = projectedPoints.begin(); it != projectedPoints.end(); ++it)
{
const mitk::Point3D &p = *it;
variances[0] += (p[0] - meanValues[0]) * (p[0] - meanValues[0]);
variances[1] += (p[1] - meanValues[1]) * (p[1] - meanValues[1]);
variances[2] += (p[2] - meanValues[2]) * (p[2] - meanValues[2]);
}
variances[0] /= (double)(projectedPoints.size() - 1);
variances[1] /= (double)(projectedPoints.size() - 1);
variances[2] /= (double)(projectedPoints.size() - 1);
// clean up
neighbourPoints->Delete();
}
// Sorts the axes of the computed orthonormal system based on
// the eigenvalues in a descending order
itk::Matrix<double, 3, 3> ComputeCovarianceMatrix(itk::Matrix<double, 3, 3> &axes,
double sigma[3],
double normalizationValue)
{
unsigned int idxMax, idxMin, idxBetween;
itk::Matrix<double, 3, 3> returnValue;
itk::Matrix<double, 3, 3> V;
itk::Matrix<double, 3, 3> diagMatrix;
diagMatrix.Fill(0.0);
if (sigma[0] >= sigma[1] && sigma[0] >= sigma[2])
{
idxMax = 0;
if (sigma[1] >= sigma[2])
{
idxBetween = 1;
idxMin = 2;
}
else
{
idxBetween = 2;
idxMin = 1;
}
}
else if (sigma[1] >= sigma[0] && sigma[1] >= sigma[2])
{
idxMax = 1;
if (sigma[0] >= sigma[2])
{
idxBetween = 0;
idxMin = 2;
}
else
{
idxBetween = 2;
idxMin = 0;
}
}
else // index 2 corresponds to largest sigma
{
idxMax = 2;
if (sigma[0] >= sigma[1])
{
idxBetween = 0;
idxMin = 1;
}
else
{
idxBetween = 1;
idxMin = 0;
}
}
V[0][0] = axes[idxMax][0];
V[1][0] = axes[idxMax][1];
V[2][0] = axes[idxMax][2];
V[0][1] = axes[idxBetween][0];
V[1][1] = axes[idxBetween][1];
V[2][1] = axes[idxBetween][2];
V[0][2] = axes[idxMin][0];
V[1][2] = axes[idxMin][1];
V[2][2] = axes[idxMin][2];
diagMatrix[0][0] = sigma[idxMax];
diagMatrix[1][1] = sigma[idxBetween];
diagMatrix[2][2] = sigma[idxMin];
returnValue = V * diagMatrix * V.GetTranspose();
if (normalizationValue > 0.0)
{
double trace = returnValue[0][0] + returnValue[1][1] + returnValue[2][2];
returnValue *= (normalizationValue / trace);
}
return returnValue;
}
diff --git a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp
index a19cad722b..0f8079384c 100644
--- a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp
@@ -1,158 +1,158 @@
/*============================================================================
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 "mitkCropTimestepsImageFilter.h"
#include <mitkImage.h>
#include <mitkArbitraryTimeGeometry.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageReadAccessor.h>
void mitk::CropTimestepsImageFilter::VerifyInputImage(const mitk::Image* inputImage) const
{
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 (inputImage->GetDimension() != 4) {
mitkThrow() << "CropTimestepsImageFilter only works with 2D+t and 3D+t images.";
}
if (inputImage->GetTimeSteps() ==1) {
mitkThrow() << "Input image has only one timestep.";
}
if (!geometry->GetImageGeometry())
mitkThrow() << "Geometry of input image is not an image geometry.";
}
void mitk::CropTimestepsImageFilter::GenerateOutputInformation()
{
Image::ConstPointer input = this->GetInput();
Image::Pointer output = this->GetOutput();
if (m_LowerBoundaryTimestep > m_UpperBoundaryTimestep) {
mitkThrow() << "lower timestep is larger than upper timestep.";
}
if (m_UpperBoundaryTimestep == std::numeric_limits<unsigned int>::max()) {
m_UpperBoundaryTimestep = input->GetTimeSteps();
}
else if (m_UpperBoundaryTimestep > input->GetTimeSteps()) {
m_UpperBoundaryTimestep = input->GetTimeSteps();
MITK_WARN << "upper boundary timestep set to " << m_UpperBoundaryTimestep;
}
m_DesiredRegion = ComputeDesiredRegion();
unsigned int dimension = input->GetDimension();
auto dimensions = new unsigned int[dimension];
itk2vtk(m_DesiredRegion.GetSize(), dimensions);
if (dimension > 3)
memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int));
dimensions[3] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep;
// create basic slicedGeometry that will be initialized below
output->Initialize(mitk::PixelType(input->GetPixelType()), dimension, dimensions);
delete[] dimensions;
auto newTimeGeometry = AdaptTimeGeometry(input->GetTimeGeometry(), m_LowerBoundaryTimestep, m_UpperBoundaryTimestep);
output->SetTimeGeometry(newTimeGeometry);
output->SetPropertyList(input->GetPropertyList());
}
mitk::SlicedData::RegionType mitk::CropTimestepsImageFilter::ComputeDesiredRegion() const
{
auto desiredRegion = this->GetInput()->GetLargestPossibleRegion();
auto index = desiredRegion.GetIndex();
auto size = desiredRegion.GetSize();
unsigned int timeDimension = 3;
index[timeDimension] = m_LowerBoundaryTimestep;
size[timeDimension] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep;
desiredRegion.SetIndex(index);
desiredRegion.SetSize(size);
return desiredRegion;
}
mitk::TimeGeometry::Pointer mitk::CropTimestepsImageFilter::AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const
{
auto newTimeGeometry = mitk::ArbitraryTimeGeometry::New();
newTimeGeometry->ClearAllGeometries();
for (unsigned int timestep = startTimestep; timestep < endTimestep; timestep++)
{
auto geometryForTimePoint = sourceGeometry->GetGeometryForTimeStep(timestep);
auto minTP = sourceGeometry->GetMinimumTimePoint(timestep);
auto maxTP = sourceGeometry->GetMaximumTimePoint(timestep);
///////////////////////////////////////
- // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
- // This workarround should be removed as soon as T28262 is solved!
+ // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
+ // This workaround should be removed as soon as T28262 is solved!
if (timestep + 1 == sourceGeometry->CountTimeSteps() && minTP == maxTP)
{
maxTP = minTP + 1.;
}
- // End of workarround for T27883
+ // End of workaround for T27883
//////////////////////////////////////
newTimeGeometry->AppendNewTimeStepClone(geometryForTimePoint, minTP, maxTP);
}
return newTimeGeometry.GetPointer();
}
void mitk::CropTimestepsImageFilter::GenerateData()
{
const auto* inputImage = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized() == false))
return;
auto timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(inputImage);
unsigned int timeStart = m_DesiredRegion.GetIndex(3);
unsigned int timeEnd = timeStart + m_DesiredRegion.GetSize(3);
for (unsigned int timestep = timeStart; timestep < timeEnd; ++timestep)
{
timeSelector->SetTimeNr(timestep);
timeSelector->UpdateLargestPossibleRegion();
mitk::ImageReadAccessor imageAccessorWithOneTimestep(timeSelector->GetOutput());
output->SetVolume(imageAccessorWithOneTimestep.GetData(), timestep-timeStart);
}
}
void mitk::CropTimestepsImageFilter::SetInput(const InputImageType* image)
{
if (this->GetInput() == image)
return;
Superclass::SetInput(image);
}
void mitk::CropTimestepsImageFilter::SetInput(unsigned int index, const InputImageType* image)
{
if (0 != index)
mitkThrow() << "Input index " << index << " is invalid.";
this->SetInput(image);
}
void mitk::CropTimestepsImageFilter::VerifyInputInformation() const
{
Superclass::VerifyInputInformation();
VerifyInputImage(this->GetInput());
}
diff --git a/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp
index 447c254832..8aca5d0ff0 100644
--- a/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp
@@ -1,414 +1,414 @@
/*============================================================================
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 "mitkHeightFieldSurfaceClipImageFilter.h"
#include "mitkImageTimeSelector.h"
#include "mitkProperties.h"
#include "mitkTimeHelper.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageToItk.h"
#include <itkImageRegionConstIterator.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkImageSliceConstIteratorWithIndex.h>
#include <vtkCellLocator.h>
#include <vtkPolyData.h>
#include <limits>
namespace mitk
{
HeightFieldSurfaceClipImageFilter::HeightFieldSurfaceClipImageFilter()
: m_ClippingMode(CLIPPING_MODE_CONSTANT),
m_ClippingConstant(0.0),
m_MultiplicationFactor(2.0),
m_MultiPlaneValue(2),
m_HeightFieldResolutionX(256),
m_HeightFieldResolutionY(256),
m_MaxHeight(1024.0)
{
this->SetNumberOfIndexedInputs(8);
this->SetNumberOfRequiredInputs(2);
m_InputTimeSelector = ImageTimeSelector::New();
m_OutputTimeSelector = ImageTimeSelector::New();
}
HeightFieldSurfaceClipImageFilter::~HeightFieldSurfaceClipImageFilter() {}
void HeightFieldSurfaceClipImageFilter::SetClippingSurface(Surface *clippingSurface)
{
this->SetNthInput(1, clippingSurface);
}
void HeightFieldSurfaceClipImageFilter::SetClippingSurfaces(ClippingPlaneList planeList)
{
if (planeList.size() > 7)
{
MITK_WARN << "Only 7 clipping planes are allowed!";
}
for (unsigned int i = 0; i < planeList.size(); ++i)
{
this->SetNthInput(i + 1, planeList.at(i));
}
}
const Surface *HeightFieldSurfaceClipImageFilter::GetClippingSurface() const
{
return dynamic_cast<const Surface *>(itk::ProcessObject::GetInput(1));
}
void HeightFieldSurfaceClipImageFilter::SetClippingMode(int mode) { m_ClippingMode = mode; }
int HeightFieldSurfaceClipImageFilter::GetClippingMode() { return m_ClippingMode; }
void HeightFieldSurfaceClipImageFilter::SetClippingModeToConstant() { m_ClippingMode = CLIPPING_MODE_CONSTANT; }
void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiplyByFactor()
{
m_ClippingMode = CLIPPING_MODE_MULTIPLYBYFACTOR;
}
void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiPlaneValue()
{
m_ClippingMode = CLIPPING_MODE_MULTIPLANE;
}
void HeightFieldSurfaceClipImageFilter::GenerateInputRequestedRegion()
{
Image *outputImage = this->GetOutput();
Image *inputImage = this->GetInput(0);
const Surface *inputSurface = dynamic_cast<const Surface *>(this->GetInput(1));
if (!outputImage->IsInitialized() || inputSurface == nullptr)
{
return;
}
inputImage->SetRequestedRegionToLargestPossibleRegion();
GenerateTimeInInputRegion(outputImage, inputImage);
}
void HeightFieldSurfaceClipImageFilter::GenerateOutputInformation()
{
const Image *inputImage = this->GetInput(0);
Image *outputImage = this->GetOutput();
if (outputImage->IsInitialized() && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
{
return;
}
itkDebugMacro(<< "GenerateOutputInformation()");
unsigned int i;
auto tmpDimensions = new unsigned int[inputImage->GetDimension()];
for (i = 0; i < inputImage->GetDimension(); ++i)
{
tmpDimensions[i] = inputImage->GetDimension(i);
}
outputImage->Initialize(
inputImage->GetPixelType(), inputImage->GetDimension(), tmpDimensions, inputImage->GetNumberOfChannels());
delete[] tmpDimensions;
outputImage->SetGeometry(static_cast<Geometry3D *>(inputImage->GetGeometry()->Clone().GetPointer()));
outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone());
m_TimeOfHeaderInitialization.Modified();
}
template <typename TPixel, unsigned int VImageDimension>
void HeightFieldSurfaceClipImageFilter::_InternalComputeClippedImage(
itk::Image<TPixel, VImageDimension> *inputItkImage,
HeightFieldSurfaceClipImageFilter *clipImageFilter,
vtkPolyData *clippingPolyData,
AffineTransform3D *imageToPlaneTransform)
{
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;
typedef itk::ImageSliceConstIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
typename ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk = ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(clipImageFilter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
std::vector<double> test;
// create the iterators
typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
// Get bounds of clipping data
clippingPolyData->ComputeBounds();
double *bounds = clippingPolyData->GetBounds();
double xWidth = bounds[1] - bounds[0];
double yWidth = bounds[3] - bounds[2];
// Create vtkCellLocator for clipping poly data
vtkCellLocator *cellLocator = vtkCellLocator::New();
cellLocator->SetDataSet(clippingPolyData);
cellLocator->CacheCellBoundsOn();
cellLocator->AutomaticOn();
cellLocator->BuildLocator();
// Allocate memory for 2D image to hold the height field generated by
// projecting the clipping data onto the plane
auto heightField = new double[m_HeightFieldResolutionX * m_HeightFieldResolutionY];
// Walk through height field and for each entry calculate height of the
// clipping poly data at this point by means of vtkCellLocator. The
// clipping data x/y bounds are used for converting from poly data space to
// image (height-field) space.
MITK_INFO << "Calculating Height Field..." << std::endl;
for (unsigned int y = 0; y < m_HeightFieldResolutionY; ++y)
{
for (unsigned int x = 0; x < m_HeightFieldResolutionX; ++x)
{
double p0[3], p1[3], surfacePoint[3], pcoords[3];
p0[0] = bounds[0] + xWidth * x / (double)m_HeightFieldResolutionX;
p0[1] = bounds[2] + yWidth * y / (double)m_HeightFieldResolutionY;
p0[2] = -m_MaxHeight;
p1[0] = p0[0];
p1[1] = p0[1];
p1[2] = m_MaxHeight;
double t, distance;
int subId;
if (cellLocator->IntersectWithLine(p0, p1, 0.1, t, surfacePoint, pcoords, subId))
{
distance = (2.0 * t - 1.0) * m_MaxHeight;
}
else
{
distance = -65536.0;
}
heightField[y * m_HeightFieldResolutionX + x] = distance;
itk::Image<double, 2>::IndexType index;
index[0] = x;
index[1] = y;
}
}
// Walk through entire input image and for each point determine its distance
// from the x/y plane.
MITK_INFO << "Performing clipping..." << std::endl;
auto factor = static_cast<TPixel>(clipImageFilter->m_MultiplicationFactor);
TPixel clippingConstant = clipImageFilter->m_ClippingConstant;
inputIt.SetFirstDirection(0);
inputIt.SetSecondDirection(1);
// through all slices
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); inputIt.NextSlice())
{
// through all lines of a slice
for (; !inputIt.IsAtEndOfSlice(); inputIt.NextLine())
{
// Transform the start(line) point from the image to the plane
Point3D imageP0, planeP0;
imageP0[0] = inputIt.GetIndex()[0];
imageP0[1] = inputIt.GetIndex()[1];
imageP0[2] = inputIt.GetIndex()[2];
planeP0 = imageToPlaneTransform->TransformPoint(imageP0);
// Transform the end point (line) from the image to the plane
Point3D imageP1, planeP1;
imageP1[0] = imageP0[0] + inputRegionOfInterest.GetSize(0);
imageP1[1] = imageP0[1];
imageP1[2] = imageP0[2];
planeP1 = imageToPlaneTransform->TransformPoint(imageP1);
// calculate the step size (if the plane is rotate, you go "crossway" through the image)
Vector3D step = (planeP1 - planeP0) / (double)inputRegionOfInterest.GetSize(0);
// over all pixel
for (; !inputIt.IsAtEndOfLine(); ++inputIt, ++outputIt, planeP0 += step)
{
// Only ConstantMode: if image pixel value == constant mode value-->set output pixel value directly
if ((clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT) &&
((TPixel)inputIt.Get() == clippingConstant))
{
outputIt.Set(clippingConstant);
}
else
{
auto x0 = (int)((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth);
auto y0 = (int)((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth);
bool clip;
- // if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel allways
+ // if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel always
if ((x0 < 0) || (x0 >= (int)m_HeightFieldResolutionX) || (y0 < 0) || (y0 >= (int)m_HeightFieldResolutionY))
{
clip = true;
}
else
{
// Calculate bilinearly interpolated height field value at plane point
int x1 = x0 + 1;
int y1 = y0 + 1;
if (x1 >= (int)m_HeightFieldResolutionX)
{
x1 = x0;
}
if (y1 >= (int)m_HeightFieldResolutionY)
{
y1 = y0;
}
// Get the neighbour points for the interpolation
ScalarType q00, q01, q10, q11;
q00 = heightField[y0 * m_HeightFieldResolutionX + x0];
q01 = heightField[y0 * m_HeightFieldResolutionX + x1];
q10 = heightField[y1 * m_HeightFieldResolutionX + x0];
q11 = heightField[y1 * m_HeightFieldResolutionX + x1];
double p00 = ((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth);
double p01 = ((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth);
ScalarType q =
q00 * ((double)x1 - p00) * ((double)y1 - p01) + q01 * (p00 - (double)x0) * ((double)y1 - p01) +
q10 * ((double)x1 - p00) * (p01 - (double)y0) + q11 * (p00 - (double)x0) * (p01 - (double)y0);
if (q - planeP0[2] < 0)
{
clip = true;
}
else
{
clip = false;
}
}
- // different modes: differnt values for the clipped pixel
+ // different modes: different values for the clipped pixel
if (clip)
{
if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT)
{
outputIt.Set(clipImageFilter->m_ClippingConstant);
}
else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLYBYFACTOR)
{
outputIt.Set(inputIt.Get() * factor);
}
else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLANE)
{
if (inputIt.Get() != 0)
outputIt.Set(inputIt.Get() + m_MultiPlaneValue);
else
outputIt.Set(inputIt.Get());
}
}
// the non-clipped pixel keeps his value
else
{
outputIt.Set(inputIt.Get());
}
}
}
}
}
MITK_INFO << "DONE!" << std::endl;
// Clean-up
cellLocator->Delete();
}
void HeightFieldSurfaceClipImageFilter::GenerateData()
{
const Image *inputImage = this->GetInput(0);
const Image *outputImage = this->GetOutput();
m_InputTimeSelector->SetInput(inputImage);
m_OutputTimeSelector->SetInput(outputImage);
Image::RegionType outputRegion = outputImage->GetRequestedRegion();
const TimeGeometry *outputTimeGeometry = outputImage->GetTimeGeometry();
const TimeGeometry *inputTimeGeometry = inputImage->GetTimeGeometry();
ScalarType timeInMS;
int timestep = 0;
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
for (unsigned int i = 1; i < this->GetNumberOfInputs(); ++i)
{
Surface *inputSurface = dynamic_cast<Surface *>(itk::ProcessObject::GetInput(i));
if (!outputImage->IsInitialized() || inputSurface == nullptr)
return;
MITK_INFO << "Plane: " << i;
MITK_INFO << "Clipping: Start\n";
// const PlaneGeometry *clippingGeometryOfCurrentTimeStep = nullptr;
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();
// Compose IndexToWorld transform of image with WorldToIndexTransform of
// clipping data for conversion from image index space to plane index space
AffineTransform3D::Pointer planeWorldToIndexTransform = AffineTransform3D::New();
inputSurface->GetGeometry(t)->GetIndexToWorldTransform()->GetInverse(planeWorldToIndexTransform);
AffineTransform3D::Pointer imageToPlaneTransform = AffineTransform3D::New();
imageToPlaneTransform->SetIdentity();
imageToPlaneTransform->Compose(inputTimeGeometry->GetGeometryForTimeStep(t)->GetIndexToWorldTransform());
imageToPlaneTransform->Compose(planeWorldToIndexTransform);
MITK_INFO << "Accessing ITK function...\n";
if (i == 1)
{
AccessByItk_3(m_InputTimeSelector->GetOutput(),
_InternalComputeClippedImage,
this,
inputSurface->GetVtkPolyData(t),
imageToPlaneTransform);
}
else
{
mitk::Image::Pointer extensionImage = m_OutputTimeSelector->GetOutput()->Clone();
AccessByItk_3(
extensionImage, _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData(t), imageToPlaneTransform);
}
if (m_ClippingMode == CLIPPING_MODE_MULTIPLANE)
m_MultiPlaneValue = m_MultiPlaneValue * 2;
}
}
m_TimeOfHeaderInitialization.Modified();
}
} // namespace
diff --git a/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp b/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp
index cdd6aba532..4ca31812c6 100755
--- a/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp
+++ b/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp
@@ -1,259 +1,259 @@
/*============================================================================
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 "mitkMovieGeneratorWin32.h"
#include <GL/gl.h>
mitk::MovieGeneratorWin32::MovieGeneratorWin32()
{
}
void mitk::MovieGeneratorWin32::SetFileName(const char *fileName)
{
m_sFile = _T(fileName);
if (_tcsstr((char *)m_sFile, _T("avi")) == nullptr)
m_sFile += _T( ".avi" );
}
void mitk::MovieGeneratorWin32::InitBitmapHeader()
{
m_width = m_renderer->GetRenderWindow()->GetSize()[0]; // changed from glGetIntegerv( GL_VIEWPORT, viewport );
m_height = m_renderer->GetRenderWindow()->GetSize()[1]; // due to sometimes strange dimensions
m_width -= 10; // remove colored boarders around renderwindows
m_height -= 10;
m_width -= m_width % 4; // some video codecs have prerequisites to the image dimensions
m_height -= m_height % 4;
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = m_width;
bih.biHeight = m_height;
bih.biPlanes = 1;
int imgSize = 3 /* BRG*/ * bih.biWidth * bih.biHeight;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biSizeImage = imgSize;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
// ASSERT(bih.biWidth%4==0);
// ASSERT(bih.biHeight%4==0);
// copying bitmap info structure.
memcpy(&m_bih, &bih, sizeof(BITMAPINFOHEADER));
}
bool mitk::MovieGeneratorWin32::InitGenerator()
{
InitBitmapHeader();
AVISTREAMINFO strHdr; // information for a single stream
AVICOMPRESSOPTIONS opts;
AVICOMPRESSOPTIONS FAR *aopts[1] = {&opts};
TCHAR szBuffer[1024];
HRESULT hr;
m_sError = _T("Ok");
// Step 0 : Let's make sure we are running on 1.1
DWORD wVer = HIWORD(VideoForWindowsVersion());
if (wVer < 0x010a)
{
// oops, we are too old, blow out of here
m_sError = _T("Version of Video for Windows too old. Come on, join the 21th century!");
return false;
}
// Step 1 : initialize AVI engine
AVIFileInit();
// Step 2 : Open the movie file for writing....
hr = AVIFileOpen(&m_pAVIFile, // Address to contain the new file interface pointer
(LPCTSTR)m_sFile, // Null-terminated string containing the name of the file to open
OF_WRITE | OF_CREATE, // Access mode to use when opening the file.
nullptr); // use handler determined from file extension.
// Name your file .avi -> very important
if (hr != AVIERR_OK)
{
_tprintf(szBuffer, _T("AVI Engine failed to initialize. Check filename %s."), m_sFile);
m_sError = szBuffer;
- // Check it succeded.
+ // Check it succeeded.
switch (hr)
{
case AVIERR_BADFORMAT:
m_sError += _T("The file couldn't be read, indicating a corrupt file or an unrecognized format.");
break;
case AVIERR_MEMORY:
m_sError += _T("The file could not be opened because of insufficient memory.");
break;
case AVIERR_FILEREAD:
m_sError += _T("A disk error occurred while reading the file.");
break;
case AVIERR_FILEOPEN:
m_sError += _T("A disk error occurred while opening the file.");
break;
case REGDB_E_CLASSNOTREG:
m_sError += _T("According to the registry, the type of file specified in AVIFileOpen does not have a handler ")
_T("to process it");
break;
}
return false;
}
// Fill in the header for the video stream....
memset(&strHdr, 0, sizeof(strHdr));
strHdr.fccType = streamtypeVIDEO; // video stream type
strHdr.fccHandler = 0;
strHdr.dwScale = 1; // should be one for video
strHdr.dwRate = static_cast<DWORD>(m_FrameRate); // fps
strHdr.dwSuggestedBufferSize = m_bih.biSizeImage; // Recommended buffer size, in bytes, for the stream.
SetRect(&strHdr.rcFrame,
0,
0, // rectangle for stream
(int)m_bih.biWidth,
(int)m_bih.biHeight);
// Step 3 : Create the stream;
hr = AVIFileCreateStream(m_pAVIFile, // file pointer
&m_pStream, // returned stream pointer
&strHdr); // stream header
- // Check it succeded.
+ // Check it succeeded.
if (hr != AVIERR_OK)
{
m_sError = _T("AVI Stream creation failed. Check Bitmap info.");
if (hr == AVIERR_READONLY)
{
m_sError += _T(" Read only file.");
}
return false;
}
// Step 4: Get codec and infos about codec
memset(&opts, 0, sizeof(opts));
// predefine MS-CRAM as standard codec
opts.fccType = streamtypeVIDEO;
// creates a video with minor quality! Use different codec (must be installed on local machine) to generate movies
// with higher quality
opts.fccHandler = mmioFOURCC('M', 'S', 'V', 'C');
opts.dwQuality = 90000; // means 90% quality; dwQuality goes from [0...10000]
-// Poping codec dialog
+// Popping codec dialog
// GUI Codec selection does not work in a vs 2005 compiled mitk, since we do not pass a hwnd as first parameter
// of AVISaveOptions
#if !(_MSC_VER >= 1400)
if (!AVISaveOptions(nullptr, 0, 1, &m_pStream, (LPAVICOMPRESSOPTIONS FAR *)&aopts))
{
AVISaveOptionsFree(1, (LPAVICOMPRESSOPTIONS FAR *)&aopts);
// return false;
}
#endif
// Step 5: Create a compressed stream using codec options.
hr = AVIMakeCompressedStream(&m_pStreamCompressed, m_pStream, &opts, nullptr);
if (hr != AVIERR_OK)
{
m_sError = _T("AVI Compressed Stream creation failed.");
switch (hr)
{
case AVIERR_NOCOMPRESSOR:
m_sError += _T(" A suitable compressor cannot be found.");
break;
case AVIERR_MEMORY:
m_sError += _T(" There is not enough memory to complete the operation.");
break;
case AVIERR_UNSUPPORTED:
m_sError += _T("Compression is not supported for this type of data. This error might be returned if you try ")
_T("to compress data that is not audio or video.");
break;
}
return false;
}
// releasing memory allocated by AVISaveOptionFree
hr = AVISaveOptionsFree(1, (LPAVICOMPRESSOPTIONS FAR *)&aopts);
if (hr != AVIERR_OK)
{
m_sError = _T("Error releasing memory");
return false;
}
// Step 6 : sets the format of a stream at the specified position
hr = AVIStreamSetFormat(m_pStreamCompressed,
0, // position
&m_bih, // stream format
m_bih.biSize + // format size
m_bih.biClrUsed * sizeof(RGBQUAD));
if (hr != AVIERR_OK)
{
m_sError = _T("AVI Compressed Stream format setting failed.");
return false;
}
// Step 6 : Initialize step counter
m_lFrame = 0;
return true;
}
bool mitk::MovieGeneratorWin32::AddFrame(void *data)
{
HRESULT hr = AVIStreamWrite(m_pStreamCompressed, // stream pointer
m_lFrame, // time of this frame
1, // number to write
(BYTE *)data, // image buffer
m_bih.biSizeImage, // size of this frame
AVIIF_KEYFRAME, // flags....
nullptr,
nullptr);
// updating frame counter
m_lFrame++;
if (hr == AVIERR_OK)
return true;
else
return false;
}
bool mitk::MovieGeneratorWin32::TerminateGenerator()
{
if (m_pStream)
{
AVIStreamRelease(m_pStream);
m_pStream = nullptr;
}
if (m_pStreamCompressed)
{
AVIStreamRelease(m_pStreamCompressed);
m_pStreamCompressed = nullptr;
}
if (m_pAVIFile)
{
AVIFileRelease(m_pAVIFile);
m_pAVIFile = nullptr;
}
// Close engine
AVIFileExit();
return true;
}
diff --git a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
index dd617de6f5..2b0c9c2c02 100644
--- a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
+++ b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
@@ -1,192 +1,192 @@
/*============================================================================
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 "mitkPlaneFit.h"
#include "mitkGeometryData.h"
#include "mitkPlaneGeometry.h"
#include <mitkProportionalTimeGeometry.h>
#include <vnl/algo/vnl_svd.h>
mitk::PlaneFit::PlaneFit() : m_PointSet(nullptr)
{
m_TimeGeometry = mitk::ProportionalTimeGeometry::New();
}
mitk::PlaneFit::~PlaneFit()
{
}
void mitk::PlaneFit::GenerateOutputInformation()
{
mitk::PointSet::ConstPointer input = this->GetInput();
mitk::GeometryData::Pointer output = this->GetOutput();
itkDebugMacro(<< "GenerateOutputInformation()");
if (input.IsNull())
return;
if (m_PointSet == nullptr)
{
return;
}
bool update = false;
if (output->GetGeometry() == nullptr || output->GetTimeGeometry() == nullptr)
update = true;
if ((!update) && (output->GetTimeGeometry()->CountTimeSteps() != input->GetTimeGeometry()->CountTimeSteps()))
update = true;
if (update)
{
mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New();
ProportionalTimeGeometry::Pointer timeGeometry =
dynamic_cast<ProportionalTimeGeometry *>(m_TimeGeometry.GetPointer());
timeGeometry->Initialize(planeGeometry, m_PointSet->GetPointSetSeriesSize());
// m_TimeGeometry->InitializeEvenlyTimed(
// planeGeometry, m_PointSet->GetPointSetSeriesSize() );
TimeStepType timeStep;
for (timeStep = 0; (timeStep < m_PointSet->GetPointSetSeriesSize()) && (timeStep < m_Planes.size()); ++timeStep)
{
timeGeometry->SetTimeStepGeometry(m_Planes[timeStep], timeStep);
}
output->SetTimeGeometry(m_TimeGeometry);
}
}
void mitk::PlaneFit::GenerateData()
{
unsigned int t;
for (t = 0; t < m_PointSet->GetPointSetSeriesSize(); ++t)
{
// check number of data points - less then 3points isn't enough
if (m_PointSet->GetSize(t) >= 3)
{
this->CalculateCentroid(t);
this->ProcessPointSet(t);
this->InitializePlane(t);
}
}
}
void mitk::PlaneFit::SetInput(const mitk::PointSet *pointSet)
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput(0, const_cast<mitk::PointSet *>(pointSet));
m_PointSet = pointSet;
unsigned int pointSetSize = pointSet->GetPointSetSeriesSize();
m_Planes.resize(pointSetSize);
m_Centroids.resize(pointSetSize);
m_PlaneVectors.resize(pointSetSize);
unsigned int t;
for (t = 0; t < pointSetSize; ++t)
{
m_Planes[t] = mitk::PlaneGeometry::New();
}
}
const mitk::PointSet *mitk::PlaneFit::GetInput()
{
if (this->GetNumberOfInputs() < 1)
{
return nullptr;
}
return static_cast<const mitk::PointSet *>(this->ProcessObject::GetInput(0));
}
void mitk::PlaneFit::CalculateCentroid(int t)
{
if (m_PointSet == nullptr)
return;
int ps_total = m_PointSet->GetSize(t);
m_Centroids[t][0] = m_Centroids[t][1] = m_Centroids[t][2] = 0.0;
for (int i = 0; i < ps_total; i++)
{
mitk::Point3D p3d = m_PointSet->GetPoint(i, t);
m_Centroids[t][0] += p3d[0];
m_Centroids[t][1] += p3d[1];
m_Centroids[t][2] += p3d[2];
}
// calculation of centroid
m_Centroids[t][0] /= ps_total;
m_Centroids[t][1] /= ps_total;
m_Centroids[t][2] /= ps_total;
}
void mitk::PlaneFit::ProcessPointSet(int t)
{
if (m_PointSet == nullptr)
return;
// int matrix with POINTS x (X,Y,Z)
vnl_matrix<mitk::ScalarType> dataM(m_PointSet->GetSize(t), 3);
int ps_total = m_PointSet->GetSize(t);
for (int i = 0; i < ps_total; i++)
{
mitk::Point3D p3d = m_PointSet->GetPoint(i, t);
dataM[i][0] = p3d[0] - m_Centroids[t][0];
dataM[i][1] = p3d[1] - m_Centroids[t][1];
dataM[i][2] = p3d[2] - m_Centroids[t][2];
}
// process the SVD (singular value decomposition) from ITK
- // the vector will be orderd descending
+ // the vector will be ordered descending
vnl_svd<mitk::ScalarType> svd(dataM, 0.0);
// calculate the SVD of A
vnl_vector<mitk::ScalarType> v = svd.nullvector();
// Avoid erratic normal sign switching when the plane changes minimally
// by negating the vector for negative x values.
if (v[0] < 0)
{
v = -v;
}
m_PlaneVectors[t][0] = v[0];
m_PlaneVectors[t][1] = v[1];
m_PlaneVectors[t][2] = v[2];
}
mitk::PlaneGeometry::Pointer mitk::PlaneFit::GetPlaneGeometry(int t)
{
return m_Planes[t];
}
const mitk::Vector3D &mitk::PlaneFit::GetPlaneNormal(int t) const
{
return m_PlaneVectors[t];
}
const mitk::Point3D &mitk::PlaneFit::GetCentroid(int t) const
{
return m_Centroids[t];
}
void mitk::PlaneFit::InitializePlane(int t)
{
m_Planes[t]->InitializePlane(m_Centroids[t], m_PlaneVectors[t]);
}
diff --git a/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp b/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp
index 26a7ed4dca..d7cc326ae0 100644
--- a/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp
@@ -1,276 +1,276 @@
/*============================================================================
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 <mitkUnstructuredGridClusteringFilter.h>
#include <vector>
#include <vtkDataArray.h>
#include <vtkDelaunay3D.h>
#include <vtkDoubleArray.h>
#include <vtkPointData.h>
#include <vtkPointLocator.h>
#include <vtkPoints.h>
#include <vtkPolyVertex.h>
#include <vtkSmartPointer.h>
#include <vtkUnstructuredGrid.h>
#include <vtkVariant.h>
mitk::UnstructuredGridClusteringFilter::UnstructuredGridClusteringFilter()
: m_eps(5.0), m_MinPts(4), m_Meshing(false), m_DistCalc(false)
{
this->m_UnstructGrid = mitk::UnstructuredGrid::New();
}
mitk::UnstructuredGridClusteringFilter::~UnstructuredGridClusteringFilter()
{
}
std::map<int, bool> visited;
std::map<int, bool> isNoise;
std::map<int, bool> clusterMember;
vtkSmartPointer<vtkPointLocator> pLocator;
std::vector<vtkSmartPointer<vtkPoints>> clusterVector;
std::vector<std::vector<int>> clustersPointsIDs;
void mitk::UnstructuredGridClusteringFilter::GenerateOutputInformation()
{
m_UnstructGrid = this->GetOutput();
}
void mitk::UnstructuredGridClusteringFilter::GenerateData()
{
mitk::UnstructuredGrid::Pointer inputGrid = const_cast<mitk::UnstructuredGrid *>(this->GetInput());
if (inputGrid.IsNull())
return;
vtkSmartPointer<vtkUnstructuredGrid> vtkInpGrid = inputGrid->GetVtkUnstructuredGrid();
vtkSmartPointer<vtkPoints> inpPoints = vtkInpGrid->GetPoints();
pLocator = vtkSmartPointer<vtkPointLocator>::New();
vtkSmartPointer<vtkDoubleArray> distances = vtkSmartPointer<vtkDoubleArray>::New();
if (inputGrid->GetVtkUnstructuredGrid()->GetPointData()->GetNumberOfArrays() > 0)
{
m_DistCalc = true;
distances = dynamic_cast<vtkDoubleArray *>(vtkInpGrid->GetPointData()->GetArray(0));
}
pLocator->SetDataSet(vtkInpGrid);
pLocator->AutomaticOn();
pLocator->SetNumberOfPointsPerBucket(2);
pLocator->BuildLocator();
// fill the visited map with false for checking
for (int i = 0; i < inpPoints->GetNumberOfPoints(); i++)
{
visited[i] = false;
isNoise[i] = false;
clusterMember[i] = false;
}
for (int i = 0; i < inpPoints->GetNumberOfPoints(); i++)
{
if (!visited[i])
{
visited[i] = true; // mark P as visited
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New(); // represent N
pLocator->FindPointsWithinRadius(m_eps, inpPoints->GetPoint(i), idList); // N = D.regionQuery(P, eps)
if (idList->GetNumberOfIds() < m_MinPts) // if sizeof(N) < MinPts
{
isNoise[i] = true; // mark P as NOISE
}
else
{
vtkSmartPointer<vtkPoints> cluster = vtkSmartPointer<vtkPoints>::New(); // represent a cluster
clusterVector.push_back(cluster); // C = next cluster
this->ExpandCluster(
i, idList, cluster, inpPoints); // expandCluster(P, N, C, eps, MinPts) mod. the parameter list
}
}
}
// OUTPUT LOGIC
m_Clusters = clusterVector;
int numberOfClusterPoints = 0;
int IdOfBiggestCluster = 0;
for (unsigned int i = 0; i < m_Clusters.size(); i++)
{
vtkSmartPointer<vtkDoubleArray> array = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkPoints> points = m_Clusters.at(i);
if (m_DistCalc)
{
array->SetNumberOfComponents(1);
array->SetNumberOfTuples(points->GetNumberOfPoints());
for (int j = 0; j < points->GetNumberOfPoints(); j++)
{
double point[3];
points->GetPoint(j, point);
if (clustersPointsIDs.at(i).at(j) < inpPoints->GetNumberOfPoints())
{
if (distances->GetValue(clustersPointsIDs.at(i).at(j)) > 0.001)
{
double dist[1] = {distances->GetValue(clustersPointsIDs.at(i).at(j))};
array->SetTuple(j, dist);
}
else
{
double dist[1] = {0.0};
array->SetTuple(j, dist);
}
}
}
m_DistanceArrays.push_back(array);
}
if (points->GetNumberOfPoints() > numberOfClusterPoints)
{
numberOfClusterPoints = points->GetNumberOfPoints();
IdOfBiggestCluster = i;
}
}
vtkSmartPointer<vtkUnstructuredGrid> biggestCluster = vtkSmartPointer<vtkUnstructuredGrid>::New();
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points = m_Clusters.at(IdOfBiggestCluster);
vtkSmartPointer<vtkPolyVertex> verts = vtkSmartPointer<vtkPolyVertex>::New();
verts->GetPointIds()->SetNumberOfIds(m_Clusters.at(IdOfBiggestCluster)->GetNumberOfPoints());
for (int i = 0; i < m_Clusters.at(IdOfBiggestCluster)->GetNumberOfPoints(); i++)
{
verts->GetPointIds()->SetId(i, i);
}
biggestCluster->Allocate(1);
biggestCluster->InsertNextCell(verts->GetCellType(), verts->GetPointIds());
biggestCluster->SetPoints(m_Clusters.at(IdOfBiggestCluster));
if (m_Meshing)
{
vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
mesher->SetInputData(biggestCluster);
mesher->SetAlpha(0.9);
mesher->Update();
vtkSmartPointer<vtkUnstructuredGrid> output = mesher->GetOutput();
m_UnstructGrid->SetVtkUnstructuredGrid(output);
}
else
{
m_UnstructGrid->SetVtkUnstructuredGrid(biggestCluster);
}
clusterVector.clear();
clustersPointsIDs.clear();
}
void mitk::UnstructuredGridClusteringFilter::ExpandCluster(int id,
vtkIdList *pointIDs,
vtkPoints *cluster,
vtkPoints *inpPoints)
{
std::vector<int> x;
x.push_back(id);
cluster->InsertNextPoint(inpPoints->GetPoint(id)); // add P to cluster C
clusterMember[id] = true;
vtkSmartPointer<vtkPoints> neighbours = vtkSmartPointer<vtkPoints>::New(); // same N as in other function
inpPoints->GetPoints(pointIDs, neighbours);
for (int i = 0; i < pointIDs->GetNumberOfIds(); i++) // for each point P' in N
{
if (!visited[pointIDs->GetId(i)]) // if P' is not visited
{
visited[pointIDs->GetId(i)] = true; // mark P' as visited
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New(); // represent N'
pLocator->FindPointsWithinRadius(
m_eps, inpPoints->GetPoint(pointIDs->GetId(i)), idList); // N' = D.regionQuery(P', eps)
if (idList->GetNumberOfIds() >= m_MinPts) // if sizeof(N') >= MinPts
{
for (int j = 0; j < idList->GetNumberOfIds(); j++) // N = N joined with N'
{
- if (idList->GetId(j) < inpPoints->GetNumberOfPoints()) // a litte bit hacked ?!
+ if (idList->GetId(j) < inpPoints->GetNumberOfPoints()) // a little bit hacked ?!
{
pointIDs->InsertNextId(idList->GetId(j));
}
}
}
}
if (!clusterMember[pointIDs->GetId(i)]) // if P' is not yet member of any cluster
{
if (pointIDs->GetId(i) < inpPoints->GetNumberOfPoints())
{
clusterMember[pointIDs->GetId(i)] = true;
x.push_back(pointIDs->GetId(i));
cluster->InsertNextPoint(inpPoints->GetPoint(pointIDs->GetId(i))); // add P' to cluster C
}
}
}
clustersPointsIDs.push_back(x);
}
std::vector<mitk::UnstructuredGrid::Pointer> mitk::UnstructuredGridClusteringFilter::GetAllClusters()
{
std::vector<mitk::UnstructuredGrid::Pointer> mitkUGridVector;
for (unsigned int i = 0; i < m_Clusters.size(); i++)
{
vtkSmartPointer<vtkUnstructuredGrid> cluster = vtkSmartPointer<vtkUnstructuredGrid>::New();
vtkSmartPointer<vtkPoints> points = m_Clusters.at(i);
vtkSmartPointer<vtkPolyVertex> verts = vtkSmartPointer<vtkPolyVertex>::New();
verts->GetPointIds()->SetNumberOfIds(points->GetNumberOfPoints());
for (int j = 0; j < points->GetNumberOfPoints(); j++)
{
verts->GetPointIds()->SetId(j, j);
}
cluster->Allocate(1);
cluster->InsertNextCell(verts->GetCellType(), verts->GetPointIds());
cluster->SetPoints(points);
if (m_DistCalc)
{
cluster->GetPointData()->AddArray(m_DistanceArrays.at(i));
}
mitk::UnstructuredGrid::Pointer mitkGrid = mitk::UnstructuredGrid::New();
if (m_Meshing)
{
vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
mesher->SetInputData(cluster);
mesher->SetAlpha(0.9);
mesher->Update();
vtkSmartPointer<vtkUnstructuredGrid> output = mesher->GetOutput();
mitkGrid->SetVtkUnstructuredGrid(output);
}
else
{
mitkGrid->SetVtkUnstructuredGrid(cluster);
}
mitkUGridVector.push_back(mitkGrid);
}
return mitkUGridVector;
}
int mitk::UnstructuredGridClusteringFilter::GetNumberOfFoundClusters()
{
return m_Clusters.size();
}
diff --git a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
index d1ceab8092..696096c8ff 100644
--- a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
+++ b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
@@ -1,459 +1,459 @@
/*============================================================================
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 "mitkWeightedPointTransform.h"
#include "mitkAnisotropicRegistrationCommon.h"
#include <vtkLandmarkTransform.h>
#include <vtkMatrix4x4.h>
#include <vtkPoints.h>
typedef itk::Matrix<double, 3, 3> Matrix3x3;
typedef std::vector<Matrix3x3> Matrix3x3List;
///////////////////////////////////////////////
// forward declarations of private functions
///////////////////////////////////////////////
static double ComputeWeightedFRE(vtkPoints *X,
vtkPoints *Y,
const Matrix3x3List &CovarianceMatricesMoving,
const Matrix3x3List &CovarianceMatricesFixed,
double FRENormalizationFactor,
Matrix3x3List &WeightMatrices,
const Matrix3x3 &rotation,
const itk::Vector<double, 3> &translation);
static void calculateWeightMatrices(const Matrix3x3List &X,
const Matrix3x3List &Y,
Matrix3x3List &result,
const Matrix3x3 &rotation);
static void IsotropicRegistration(vtkPoints *X,
vtkPoints *Y,
vtkLandmarkTransform *landmarkTransform,
Matrix3x3 &rotation,
itk::Vector<double, 3> &translation);
mitk::WeightedPointTransform::WeightedPointTransform()
: m_Threshold(1.0e-4),
m_MaxIterations(1000),
m_Iterations(-1),
m_FRE(-1.0),
m_FRENormalizationFactor(1.0),
m_LandmarkTransform(vtkSmartPointer<vtkLandmarkTransform>::New())
{
}
mitk::WeightedPointTransform::~WeightedPointTransform()
{
m_FixedPointSet = nullptr;
m_MovingPointSet = nullptr;
m_LandmarkTransform = nullptr;
}
void mitk::WeightedPointTransform::ComputeTransformation()
{
WeightedPointRegister(m_MovingPointSet,
m_FixedPointSet,
m_CovarianceMatricesMoving,
m_CovarianceMatricesFixed,
m_Threshold,
m_MaxIterations,
m_Rotation,
m_Translation,
m_FRE,
m_Iterations);
}
// computes the weightmatrix with 2 covariance matrices
// and a given transformation
void calculateWeightMatrices(const Matrix3x3List &X,
const Matrix3x3List &Y,
Matrix3x3List &result,
const Matrix3x3 &rotation)
{
const vnl_matrix_fixed<double, 3, 3> rotation_T = rotation.GetTranspose();
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(X.size()); ++i)
{
const Matrix3x3 w = rotation * X[i] * rotation_T;
result[i] = mitk::AnisotropicRegistrationCommon::CalculateWeightMatrix(w, Y[i]);
}
}
// computes the weighted fiducial registration error
double ComputeWeightedFRE(vtkPoints *X,
vtkPoints *Y,
const Matrix3x3List &CovarianceMatricesMoving,
const Matrix3x3List &CovarianceMatricesFixed,
double FRENormalizationFactor,
Matrix3x3List &WeightMatrices,
const Matrix3x3 &rotation,
const itk::Vector<double, 3> &translation)
{
double FRE = 0;
// compute weighting matrices
calculateWeightMatrices(CovarianceMatricesMoving, CovarianceMatricesFixed, WeightMatrices, rotation);
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(WeightMatrices.size()); ++i)
{
- // convert to itk data types (nessecary since itk 4 migration)
+ // convert to itk data types (necessary since itk 4 migration)
itk::Vector<double, 3> converted_MovingPoint;
double point[3];
X->GetPoint(i, point);
converted_MovingPoint[0] = point[0];
converted_MovingPoint[1] = point[1];
converted_MovingPoint[2] = point[2];
// transform point
itk::Vector<double, 3> p = rotation * converted_MovingPoint + translation;
Y->GetPoint(i, point);
p[0] -= point[0];
p[1] -= point[1];
p[2] -= point[2];
// do calculation
const itk::Vector<double, 3> D = WeightMatrices.at(i) * p;
#pragma omp critical
FRE += (D[0] * D[0] + D[1] * D[1] + D[2] * D[2]);
}
FRE /= WeightMatrices.size();
FRE = FRENormalizationFactor * sqrt(FRE);
return FRE;
}
// registers two pointsets with an isotropic landmark transform
void IsotropicRegistration(vtkPoints *X,
vtkPoints *Y,
vtkLandmarkTransform *landmarkTransform,
Matrix3x3 &rotation,
itk::Vector<double, 3> &translation)
{
landmarkTransform->SetSourceLandmarks(X);
landmarkTransform->SetTargetLandmarks(Y);
landmarkTransform->SetModeToRigidBody();
landmarkTransform->Modified();
landmarkTransform->Update();
vtkMatrix4x4 *m = landmarkTransform->GetMatrix();
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
rotation[i][j] = m->GetElement(i, j);
translation[0] = m->GetElement(0, 3);
translation[1] = m->GetElement(1, 3);
translation[2] = m->GetElement(2, 3);
}
void mitk::WeightedPointTransform::C_maker(vtkPoints *X,
const WeightMatrixList &W,
itk::VariableSizeMatrix<double> &returnValue)
{
#pragma omp parallel for
for (int i = 0; i < X->GetNumberOfPoints(); ++i)
{
unsigned int index = 3u * i;
double point[3];
X->GetPoint(i, point);
for (int j = 0; j < 3; ++j)
{
returnValue[index][0] = -W.at(i)[j][1] * point[2] + W.at(i)[j][2] * point[1];
returnValue[index][1] = W.at(i)[j][0] * point[2] - W.at(i)[j][2] * point[0];
returnValue[index][2] = -W.at(i)[j][0] * point[1] + W.at(i)[j][1] * point[0];
returnValue[index][3] = W.at(i)[j][0];
returnValue[index][4] = W.at(i)[j][1];
returnValue[index][5] = W.at(i)[j][2];
index += 1;
}
}
}
void mitk::WeightedPointTransform::E_maker(vtkPoints *X,
vtkPoints *Y,
const WeightMatrixList &W,
vnl_vector<double> &returnValue)
{
#pragma omp parallel for
for (int i = 0; i < X->GetNumberOfPoints(); ++i)
{
unsigned int index = 3u * i;
double pX[3];
double pY[3];
Matrix3x3 M;
X->GetPoint(i, pX);
Y->GetPoint(i, pY);
M[0][0] = pY[0] - pX[0];
M[0][1] = pY[1] - pX[1];
M[0][2] = pY[2] - pX[2];
M[1][0] = M[0][0];
M[1][1] = M[0][1];
M[1][2] = M[0][2];
M[2][0] = M[0][0];
M[2][1] = M[0][1];
M[2][2] = M[0][2];
for (unsigned int j = 0; j < 3; ++j)
{
returnValue[index + j] = W.at(i)[j][0] * M[j][0] + W.at(i)[j][1] * M[j][1] + W.at(i)[j][2] * M[j][2];
}
}
}
void mitk::WeightedPointTransform::WeightedPointRegister(vtkPoints *X,
vtkPoints *Y,
const CovarianceMatrixList &Sigma_X,
const CovarianceMatrixList &Sigma_Y,
double Threshold,
int MaxIterations,
Rotation &TransformationR,
Translation &TransformationT,
double &FRE,
int &n)
{
double FRE_identity = 0.0;
double FRE_isotropic_weighted = 0.0;
double initialFRE = 0.0;
// set config_change to infinite (max double) at start
double config_change = std::numeric_limits<double>::max();
Rotation initial_TransformationR;
initial_TransformationR.SetIdentity();
Translation initial_TransformationT;
initial_TransformationT.Fill(0.0);
// Weightmatrices
Matrix3x3List W;
vtkPoints *X_transformed = vtkPoints::New();
vtkPoints *X_transformedNew = vtkPoints::New();
vnl_vector<double> oldq;
itk::VariableSizeMatrix<double> iA;
vnl_vector<double> iB;
// initialize memory
W.resize(X->GetNumberOfPoints());
X_transformed->SetNumberOfPoints(X->GetNumberOfPoints());
X_transformedNew->SetNumberOfPoints(X->GetNumberOfPoints());
iA.SetSize(3u * X->GetNumberOfPoints(), 6u);
iB.set_size(3u * X->GetNumberOfPoints());
// calculate FRE_0 with identity transform
FRE_identity = ComputeWeightedFRE(
X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, initial_TransformationR, initial_TransformationT);
MITK_DEBUG << "FRE for identity transform: " << FRE_identity;
// compute isotropic transformation as initial estimate
IsotropicRegistration(X, Y, m_LandmarkTransform, initial_TransformationR, initial_TransformationT);
// result of unweighted registration algorithm
TransformationR = initial_TransformationR;
TransformationT = initial_TransformationT;
// calculate FRE_0 with isotropic transform
FRE_isotropic_weighted =
ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT);
MITK_DEBUG << "FRE for transform obtained with unweighted registration: " << FRE_isotropic_weighted;
// if R,t is worse than the identity, use the identity as initial transform
if (FRE_isotropic_weighted < FRE_identity)
{
initialFRE = FRE_isotropic_weighted;
}
else
{
initialFRE = FRE_identity;
TransformationR.SetIdentity(); // set rotation to identity element
TransformationT.Fill(0.0); // set translation to identity element
initial_TransformationR.SetIdentity();
initial_TransformationT.Fill(0.0);
}
// apply transform to moving set:
mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformed, TransformationR, TransformationT);
// start with iteration 0
n = 0;
do
{
n++;
calculateWeightMatrices(Sigma_X, Sigma_Y, W, TransformationR);
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// PROBLEM: no square matrix but the backslash operator in matlab does solve the system anyway. How to convert this
// to C++?
- // good descriptons to the "backslash"-operator (in german):
+ // good descriptions to the "backslash"-operator (in german):
// http://www.tm-mathe.de/Themen/html/matlab__zauberstab__backslash-.html
// http://www.tm-mathe.de/Themen/html/matlab__matrix-division__vorsi.html#HoheMatrixA
//
// current method: treat the problem as a minimization problem, because this is what the
// "backslash"-operator also does with "high" matrices.
// (and we will have those matrices in most cases)
C_maker(X_transformed, W, iA);
E_maker(X_transformed, Y, W, iB);
vnl_matrix_inverse<double> myInverse(iA.GetVnlMatrix());
vnl_vector<double> q = myInverse.pinverse(iB.size()) * iB;
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
if (n > 1)
q = (q + oldq) / 2;
oldq = q;
itk::Vector<double, 3> delta_t;
delta_t[0] = q[3];
delta_t[1] = q[4];
delta_t[2] = q[5];
Matrix3x3 delta_theta;
delta_theta[0][0] = 1;
delta_theta[0][1] = -q[2];
delta_theta[0][2] = q[1];
delta_theta[1][0] = q[2];
delta_theta[1][1] = 1;
delta_theta[1][2] = -q[0];
delta_theta[2][0] = -q[1];
delta_theta[2][1] = q[0];
delta_theta[2][2] = 1;
vnl_svd<double> svd_delta_theta(delta_theta.GetVnlMatrix().as_ref());
// convert vnl matrices to itk matrices...
Matrix3x3 U;
Matrix3x3 V;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
U[i][j] = svd_delta_theta.U()[i][j];
V[i][j] = svd_delta_theta.V()[i][j];
}
}
Matrix3x3 delta_R = U * V.GetTranspose();
// update rotation
TransformationR = delta_R * TransformationR;
// update translation
TransformationT = delta_R * TransformationT + delta_t;
// update moving points
mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformedNew, TransformationR, TransformationT);
// calculate config change
config_change = CalculateConfigChange(X_transformed, X_transformedNew);
// swap the pointers the old set for the next iteration is
// the new set of the last iteration
vtkPoints *tmp = X_transformed;
X_transformed = X_transformedNew;
X_transformedNew = tmp;
} while (config_change > Threshold && n < MaxIterations);
// calculate FRE with current transform
FRE = ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT);
MITK_DEBUG << "FRE after algorithm (prior to check with initial): " << FRE;
// compare with FRE_initial
if (initialFRE < FRE)
{
MITK_WARN << "FRE did not improve in anisotropic point registration function";
TransformationR = initial_TransformationR;
TransformationT = initial_TransformationT;
FRE = initialFRE;
}
MITK_DEBUG << "FRE final: " << FRE;
X_transformed->Delete();
X_transformedNew->Delete();
}
void mitk::WeightedPointTransform::SetMovingPointSet(vtkSmartPointer<vtkPoints> p)
{
m_MovingPointSet = p;
}
void mitk::WeightedPointTransform::SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices)
{
m_CovarianceMatricesMoving = matrices;
}
void mitk::WeightedPointTransform::SetFixedPointSet(vtkSmartPointer<vtkPoints> p)
{
m_FixedPointSet = p;
}
void mitk::WeightedPointTransform::SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices)
{
m_CovarianceMatricesFixed = matrices;
}
double mitk::WeightedPointTransform::CalculateConfigChange(vtkPoints *X, vtkPoints *X_new)
{
double sum[3] = {0.0, 0.0, 0.0};
double mean[3] = {0.0, 0.0, 0.0};
double pX[3] = {0.0, 0.0, 0.0};
double pX_new[3] = {0.0, 0.0, 0.0};
// compute mean of the old point set and the first sum
for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i)
{
X->GetPoint(i, pX);
X_new->GetPoint(i, pX_new);
// first sum
sum[0] += (pX_new[0] - pX[0]) * (pX_new[0] - pX[0]);
sum[1] += (pX_new[1] - pX[1]) * (pX_new[1] - pX[1]);
sum[2] += (pX_new[2] - pX[2]) * (pX_new[2] - pX[2]);
// mean
mean[0] += pX[0];
mean[1] += pX[1];
mean[2] += pX[2];
}
mean[0] /= X->GetNumberOfPoints();
mean[1] /= X->GetNumberOfPoints();
mean[2] /= X->GetNumberOfPoints();
const double us = sum[0] + sum[1] + sum[2];
// reset sum
sum[0] = sum[1] = sum[2] = 0.0;
for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i)
{
X->GetPoint(i, pX);
sum[0] += (pX[0] - mean[0]) * (pX[0] - mean[0]);
sum[1] += (pX[1] - mean[1]) * (pX[1] - mean[1]);
sum[2] += (pX[2] - mean[2]) * (pX[2] - mean[2]);
}
const double ls = sum[0] + sum[1] + sum[2];
return sqrt(us / ls);
}
diff --git a/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp b/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp
index c916881b8e..69383d9709 100644
--- a/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp
@@ -1,168 +1,168 @@
/*============================================================================
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 <mitkIOUtil.h>
#include <mitkSurface.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <vtkCleanPolyData.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkTransform.h>
#include "mitkAnisotropicIterativeClosestPointRegistration.h"
#include "mitkAnisotropicRegistrationCommon.h"
#include "mitkCovarianceMatrixCalculator.h"
/**
* Test to verify the results of the A-ICP registration.
* The test runs the standard A-ICP and the trimmed variant.
*/
class mitkAnisotropicIterativeClosestPointRegistrationTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkAnisotropicIterativeClosestPointRegistrationTestSuite);
MITK_TEST(testAicpRegistration);
MITK_TEST(testTrimmedAicpregistration);
CPPUNIT_TEST_SUITE_END();
private:
typedef itk::Matrix<double, 3, 3> Matrix3x3;
typedef itk::Vector<double, 3> Vector3;
typedef std::vector<Matrix3x3> CovarianceMatrixList;
mitk::Surface::Pointer m_MovingSurface;
mitk::Surface::Pointer m_FixedSurface;
mitk::PointSet::Pointer m_TargetsMovingSurface;
mitk::PointSet::Pointer m_TargetsFixedSurface;
CovarianceMatrixList m_SigmasMovingSurface;
CovarianceMatrixList m_SigmasFixedSurface;
double m_FRENormalizationFactor;
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.
+ * correct and new initialization 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
{
mitk::CovarianceMatrixCalculator::Pointer matrixCalculator = mitk::CovarianceMatrixCalculator::New();
m_MovingSurface = mitk::IOUtil::Load<mitk::Surface>(GetTestDataFilePath("AICPRegistration/head_green.vtp"));
m_FixedSurface = mitk::IOUtil::Load<mitk::Surface>(GetTestDataFilePath("AICPRegistration/head_red.vtp"));
m_TargetsMovingSurface = mitk::IOUtil::Load<mitk::PointSet>(GetTestDataFilePath("AICPRegistration/targets_head_green.mps"));
m_TargetsFixedSurface = mitk::IOUtil::Load<mitk::PointSet>(GetTestDataFilePath("AICPRegistration/targets_head_red.mps"));
// compute covariance matrices
matrixCalculator->SetInputSurface(m_MovingSurface);
matrixCalculator->ComputeCovarianceMatrices();
m_SigmasMovingSurface = matrixCalculator->GetCovarianceMatrices();
const double meanVarX = matrixCalculator->GetMeanVariance();
matrixCalculator->SetInputSurface(m_FixedSurface);
matrixCalculator->ComputeCovarianceMatrices();
m_SigmasFixedSurface = matrixCalculator->GetCovarianceMatrices();
const double meanVarY = matrixCalculator->GetMeanVariance();
m_FRENormalizationFactor = sqrt(meanVarX + meanVarY);
}
void tearDown() override
{
m_MovingSurface = nullptr;
m_FixedSurface = nullptr;
m_TargetsMovingSurface = nullptr;
m_TargetsFixedSurface = nullptr;
m_SigmasMovingSurface.clear();
m_SigmasFixedSurface.clear();
}
void testAicpRegistration()
{
const double expFRE = 26.3453;
const double expTRE = 3.8707;
mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
mitk::AnisotropicIterativeClosestPointRegistration::New();
// set up parameters
aICP->SetMovingSurface(m_MovingSurface);
aICP->SetFixedSurface(m_FixedSurface);
aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
aICP->SetThreshold(0.000001);
// run the algorithm
aICP->Update();
MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
mitk::Equal(aICP->GetFRE(), expFRE, 0.0001));
// compute the target registration Error
const double tre =
mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(m_TargetsMovingSurface.GetPointer(),
m_TargetsFixedSurface.GetPointer(),
aICP->GetRotation(),
aICP->GetTranslation());
// MITK_INFO << "R:\n" << aICP->GetRotation() << "T: "<< aICP->GetTranslation();
MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
mitk::Equal(tre, expTRE, 0.00001));
}
void testTrimmedAicpregistration()
{
const double expFRE = 18.5469;
const double expTRE = 5.5871;
mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
mitk::AnisotropicIterativeClosestPointRegistration::New();
// Swap X and Y for partial overlapping registration
aICP->SetMovingSurface(m_MovingSurface);
aICP->SetFixedSurface(m_FixedSurface);
aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
aICP->SetThreshold(0.000001);
aICP->SetTrimmFactor(0.80);
// run the algorithm
aICP->Update();
MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
mitk::Equal(aICP->GetFRE(), expFRE, 0.01));
// compute the target registration Error
const double tre =
mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(m_TargetsMovingSurface.GetPointer(),
m_TargetsFixedSurface.GetPointer(),
aICP->GetRotation(),
aICP->GetTranslation());
MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
mitk::Equal(tre, expTRE, 0.01));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkAnisotropicIterativeClosestPointRegistration)
diff --git a/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp b/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp
index 0651db2802..cb3c5493d3 100644
--- a/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp
@@ -1,120 +1,120 @@
/*============================================================================
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 <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkImageDataItem.h>
#include <mitkBoundingObject.h>
#include <mitkBoundingObjectCutter.h>
#include <mitkCuboid.h>
#include <itkImage.h>
#include <fstream>
#include <vtkImageData.h>
#include <mitkTestingMacros.h>
int mitkBoundingObjectCutterTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN(mitkBoundingObjectCutterTest);
////Create Image out of nowhere
// mitk::Image::Pointer image;
// mitk::PixelType pt(mitk::MakeScalarPixelType<int>() );
// unsigned int dim[]={100,100,20};
- // MITK_TEST_OUTPUT(<< "Creating Image as imput for cutting: ");
+ // MITK_TEST_OUTPUT(<< "Creating Image as input for cutting: ");
// image=mitk::Image::New();
// image->Initialize(mitk::MakeScalarPixelType<int>(), 3, dim);
// mitk::ImageReadAccessor imgAcc(image);
// int *p = (int*)imgAcc.GetData();
// unsigned int i;
// unsigned int size = dim[0]*dim[1]*dim[2];
// for(i=0; i<size; ++i, ++p)
// *p= (signed int)i;
// std::cout<<"[PASSED]"<<std::endl;
// MITK_TEST_OUTPUT(<< "Testing mitk::BoundingObject::FitGeometry(image->GetGeometry()) with an mitk::Cuboid
// (sub-class of mitk::BoundingObject): ");
// mitk::Cuboid::Pointer cuboid = mitk::Cuboid::New();
// cuboid->FitGeometry(image->GetGeometry());
// std::cout<<"[PASSED]"<<std::endl;
// MITK_TEST_OUTPUT(<< "Testing whether corners of the cuboid are identical to corners of the image: ");
// int c;
// for(c=0; c<6; ++c)
// {
// MITK_TEST_OUTPUT(<< " Testing GetCornerPoint(" << c << "): ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetCornerPoint(c),cuboid->GetGeometry()->GetCornerPoint(c)-1), "");
// }
// MITK_TEST_OUTPUT(<< "Testing whether diagonal^2 of fitted mitk::Cuboid is identical to diagonal^2 of image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetDiagonalLength2(),cuboid->GetGeometry()->GetDiagonalLength2()), "");
// MITK_TEST_OUTPUT(<< "Testing mitk::BoundingObjectCutter: ");
// mitk::BoundingObjectCutter::Pointer boCutter = mitk::BoundingObjectCutter::New();
// boCutter->SetInput(image);
// boCutter->SetBoundingObject(cuboid);
// MITK_TEST_OUTPUT(<< " Testing mitk::BoundingObjectCutter::UpdateLargestPossibleRegion():: ");
// boCutter->UpdateLargestPossibleRegion();
// std::cout<<"[PASSED]"<<std::endl;
// mitk::Image::Pointer cuttedImage = boCutter->GetOutput();
// MITK_TEST_OUTPUT(<< " Testing whether origin of cutted image is identical to origin of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetOrigin(),cuttedImage->GetGeometry()->GetOrigin()), "");
// MITK_TEST_OUTPUT(<< " Testing whether spacing of cutted image is identical to spacing of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetSpacing(),cuttedImage->GetGeometry()->GetSpacing()), "");
// MITK_TEST_OUTPUT(<< " Testing whether center of cutted image is identical to center of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetCenter(),cuttedImage->GetGeometry()->GetCenter()), "");
// MITK_TEST_OUTPUT(<< " Testing whether diagonal^2 of cutted image is identical to diagonal^2 of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetDiagonalLength2(),cuttedImage->GetGeometry()->GetDiagonalLength2()), "");
// MITK_TEST_OUTPUT(<< " Testing whether corners of cutted image are identical to corners of original image: ");
// for(c=0; c<6; ++c)
//{
// MITK_TEST_OUTPUT(<< " Testing GetCornerPoint(" << c << "): ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetCornerPoint(c),cuttedImage->GetGeometry()->GetCornerPoint(c)), "");
//}
// MITK_TEST_OUTPUT(<< " Testing whether pixel data of cutted image are identical to pixel data of original image: ");
// mitk::ImageReadAccessor imgAcc(image);
// p = (int*)imgAcc.GetData();
// mitk::ImageReadAccessor cuttedImage(cuttedImage);
// int *pCutted = (int*)cuttedImage.GetData();
// for(i=0; i<size; ++i, ++p, ++pCutted)
//{
// if(*p!=*pCutted)
// break;
//}
// MITK_TEST_CONDITION_REQUIRED(i==size, "");
// MITK_TEST_OUTPUT(<< " Testing whether geometry of cutted image has ImageGeometry==true: ");
// MITK_TEST_CONDITION_REQUIRED(cuttedImage->GetGeometry()->GetImageGeometry(), "");
MITK_TEST_END();
return EXIT_SUCCESS;
}
diff --git a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp b/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp
index 84373e97d8..80644df37c 100644
--- a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp
@@ -1,79 +1,79 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include <mitkImageToUnstructuredGridFilter.h>
#include <mitkTestFixture.h>
#include <mitkIOUtil.h>
class mitkImageToUnstructuredGridFilterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageToUnstructuredGridFilterTestSuite);
MITK_TEST(testImageToUnstructuredGridFilterInitialization);
MITK_TEST(testInput);
MITK_TEST(testUnstructuredGridGeneration);
MITK_TEST(testThreshold);
CPPUNIT_TEST_SUITE_END();
private:
/** Members used inside the different test methods. All members are initialized via setUp().*/
mitk::Image::Pointer m_BallImage;
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_BallImage = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("BallBinary30x30x30.nrrd")); }
void testImageToUnstructuredGridFilterInitialization()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing initialization of threshold member variable", testFilter->GetThreshold() == -0.1);
}
void testInput()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
testFilter->SetInput(m_BallImage);
CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_BallImage);
}
void testUnstructuredGridGeneration()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
testFilter->SetInput(m_BallImage);
testFilter->Update();
CPPUNIT_ASSERT_MESSAGE("Testing UnstructuredGrid generation!", testFilter->GetOutput() != nullptr);
}
void testThreshold()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter1 = mitk::ImageToUnstructuredGridFilter::New();
testFilter1->SetInput(m_BallImage);
testFilter1->Update();
int numberOfPoints1 = testFilter1->GetNumberOfExtractedPoints();
mitk::ImageToUnstructuredGridFilter::Pointer testFilter2 = mitk::ImageToUnstructuredGridFilter::New();
testFilter2->SetInput(m_BallImage);
testFilter2->SetThreshold(1.0);
testFilter2->Update();
int numberOfPoints2 = testFilter2->GetNumberOfExtractedPoints();
CPPUNIT_ASSERT_MESSAGE("Testing Threshold", numberOfPoints1 > numberOfPoints2);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkImageToUnstructuredGridFilter)
diff --git a/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp b/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp
index bbbb62cb99..34d9ff5133 100644
--- a/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp
@@ -1,88 +1,88 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include <fstream>
#include <mitkGeometry3D.h>
#include <mitkGeometryData.h>
#include <mitkNumericTypes.h>
#include <mitkPlaneFit.h>
#include <mitkPlaneGeometry.h>
#include <mitkPointSet.h>
int mitkPlaneFitTest(int, char *[])
{
// float bounds[]={0.0f,10.0f,0.0f,10.0f,0.0f,5.0f};
mitk::PlaneFit::Pointer PlaneFit = mitk::PlaneFit::New();
mitk::PointSet::Pointer PointSet = mitk::PointSet::New();
mitk::Point3D Point;
- // first without any point, then incrementally add points within thre points there will be a plane geometry
+ // first without any point, then incrementally add points within the points there will be a plane geometry
std::cout << "Start PlaneFitTest " << std::endl;
for (int position = 0; position < 6; position++)
{
// add a point directly
mitk::FillVector3D(Point, (float)position, (float)position * 1.5, 2.5);
PointSet->GetPointSet()->GetPoints()->InsertElement(position, Point);
}
// Set Input
PlaneFit->SetInput(PointSet);
const mitk::PointSet *testPointSet = PlaneFit->GetInput();
std::cout << " Size test of Input Method: ";
if (testPointSet->GetSize() == PointSet->GetSize())
{
std::cout << "[PASSED]" << std::endl;
}
else
{
std::cout << "[FAILED]" << std::endl;
return EXIT_FAILURE;
}
// Test Centroid
std::cout << " Testing centroid calculaation: ";
PlaneFit->Update();
const mitk::Point3D &centroid = PlaneFit->GetCentroid();
mitk::Point3D expectedCentroid;
expectedCentroid[0] = 2.5;
expectedCentroid[1] = 3.75;
expectedCentroid[2] = 2.5;
if (centroid == expectedCentroid)
{
std::cout << "[PASSED]" << std::endl;
}
else
{
std::cout << "[FAILED]" << std::endl;
return EXIT_FAILURE;
}
// Test PlaneGeometry
std::cout << " Test PlaneGeometry: ";
auto *PlaneGeometry = dynamic_cast<mitk::PlaneGeometry *>(PlaneFit->GetOutput()->GetGeometry());
if (PlaneGeometry)
{
std::cout << "[PASSED]" << std::endl;
}
else
{
std::cout << "[FAILED]" << std::endl;
return EXIT_FAILURE;
}
std::cout << "[TEST DONE]" << std::endl;
return EXIT_SUCCESS;
}
diff --git a/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp b/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp
index 4e0959183e..d4207c45fe 100644
--- a/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp
@@ -1,43 +1,43 @@
/*============================================================================
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 <mitkSimpleHistogram.h>
#include <mitkSurface.h>
#include <mitkTestingMacros.h>
int mitkSimpleHistogramTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkSimpleHistogram");
auto myTestSimpleImageHistogram = new mitk::SimpleImageHistogram();
- MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram != nullptr, "Testing instanciation.");
+ MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram != nullptr, "Testing instantiation.");
MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram->GetMax() == 1, "Testing GetMax().");
MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram->GetMin() == 0, "Testing GetMin().");
MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram->GetRelativeBin(1.0, 5.0) == 0, "Testing GetRelativeBin().");
bool success = true;
try
{
myTestSimpleImageHistogram->ComputeFromBaseData(nullptr);
myTestSimpleImageHistogram->ComputeFromBaseData(mitk::Image::New()); // an empty image
myTestSimpleImageHistogram->ComputeFromBaseData(mitk::Surface::New()); // an invalid value
}
catch (...)
{
success = false;
}
MITK_TEST_CONDITION_REQUIRED(success, "Testing ComputeFromBaseData() with invalid input values.");
MITK_TEST_CONDITION_REQUIRED(!myTestSimpleImageHistogram->GetValid(),
"Testing if histogram is invalid after invalid input.");
MITK_TEST_END();
}
diff --git a/Modules/Annotation/include/mitkLabelAnnotation3D.h b/Modules/Annotation/include/mitkLabelAnnotation3D.h
index 7b28a20386..ad25755325 100644
--- a/Modules/Annotation/include/mitkLabelAnnotation3D.h
+++ b/Modules/Annotation/include/mitkLabelAnnotation3D.h
@@ -1,109 +1,109 @@
/*============================================================================
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 mitkLabelAnnotation3D_h
#define mitkLabelAnnotation3D_h
#include "MitkAnnotationExports.h"
#include <mitkLocalStorageHandler.h>
#include <mitkVtkAnnotation3D.h>
#include <vtkSmartPointer.h>
class vtkStringArray;
class vtkPolyDataMapper;
class vtkPolyData;
class vtkActor2D;
class vtkProperty2D;
class vtkPointSetToLabelHierarchy;
class vtkLabelPlacementMapper;
class vtkIntArray;
namespace mitk
{
class PointSet;
/** \brief Can display a high amount of 3D labels to a PointSet */
class MITKANNOTATION_EXPORT LabelAnnotation3D : public mitk::VtkAnnotation3D
{
public:
/** \brief Internal class holding the vtkActor, etc. for each of the render windows */
class LocalStorage : public mitk::Annotation::BaseLocalStorage
{
public:
vtkSmartPointer<vtkPolyData> m_Points;
vtkSmartPointer<vtkActor2D> m_LabelsActor;
vtkSmartPointer<vtkIntArray> m_Sizes;
vtkSmartPointer<vtkStringArray> m_Labels;
vtkSmartPointer<vtkLabelPlacementMapper> m_LabelMapper;
vtkSmartPointer<vtkPointSetToLabelHierarchy> m_PointSetToLabelHierarchyFilter;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastUpdateTime;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default deconstructor of the local storage. */
~LocalStorage();
};
mitkClassMacro(LabelAnnotation3D, mitk::VtkAnnotation3D);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Set the vector of labels that are shown to each corresponding point3D. The size has to be equal to the
provided LabelCoordinates. */
void SetLabelVector(const std::vector<std::string> &LabelVector);
/** \brief Optional: Provide a vector of priorities. The labels with higher priorities will be visible in lower LOD
*/
void SetPriorityVector(const std::vector<int> &PriorityVector);
/** \brief Coordinates of the labels */
void SetLabelCoordinates(itk::SmartPointer<PointSet> LabelCoordinates);
void PointSetModified(const itk::Object *, const itk::EventObject &);
protected:
/** \brief The LocalStorageHandler holds all LocalStorages for the render windows. */
mutable mitk::LocalStorageHandler<LocalStorage> m_LSH;
vtkProp *GetVtkProp(BaseRenderer *renderer) const override;
void UpdateVtkAnnotation(mitk::BaseRenderer *renderer) override;
/** \brief explicit constructor which disallows implicit conversions */
explicit LabelAnnotation3D();
/** \brief virtual destructor in order to derive from this class */
~LabelAnnotation3D() override;
private:
/** \brief The char arrays in this vector are displayed at the corresponding coordinates.*/
std::vector<std::string> m_LabelVector;
- /** \brief values in this array set a priority to each label. Higher priority labels are not covert by labels with
+ /** \brief values in this array set a priority to each label. Higher priority labels are not convert by labels with
* lower priority.*/
std::vector<int> m_PriorityVector;
/** \brief The coordinates of the labels. Indices must match the labelVector and the priorityVector.*/
itk::SmartPointer<PointSet> m_LabelCoordinates;
unsigned long m_PointSetModifiedObserverTag;
/** \brief copy constructor */
LabelAnnotation3D(const LabelAnnotation3D &);
/** \brief assignment operator */
LabelAnnotation3D &operator=(const LabelAnnotation3D &);
};
} // namespace mitk
#endif
diff --git a/Modules/Annotation/test/mitkAnnotationTest.cpp b/Modules/Annotation/test/mitkAnnotationTest.cpp
index b2e19cc085..7de6db0b26 100644
--- a/Modules/Annotation/test/mitkAnnotationTest.cpp
+++ b/Modules/Annotation/test/mitkAnnotationTest.cpp
@@ -1,55 +1,55 @@
/*============================================================================
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 <mitkCommon.h>
#include <mitkIOUtil.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include "mitkManualPlacementAnnotationRenderer.h"
#include "mitkLayoutAnnotationRenderer.h"
class mitkAnnotationTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkAnnotationTestSuite);
MITK_TEST(AnnotationUtilsTest);
CPPUNIT_TEST_SUITE_END();
private:
public:
void setUp() override {}
void AnnotationUtilsTest()
{
mitk::ManualPlacementAnnotationRenderer *ap1_test1 = mitk::ManualPlacementAnnotationRenderer::GetAnnotationRenderer("test1");
CPPUNIT_ASSERT_MESSAGE("Testing availability of ManualPlacementAnnotationRenderer service", ap1_test1);
mitk::ManualPlacementAnnotationRenderer *ap2_test1 = mitk::ManualPlacementAnnotationRenderer::GetAnnotationRenderer("test1");
- CPPUNIT_ASSERT_MESSAGE("Testing if ManualPlacementAnnotationRenderer of same kind stays avaliable", ap1_test1 == ap2_test1);
+ CPPUNIT_ASSERT_MESSAGE("Testing if ManualPlacementAnnotationRenderer of same kind stays available", ap1_test1 == ap2_test1);
mitk::ManualPlacementAnnotationRenderer *ap1_test2 = mitk::ManualPlacementAnnotationRenderer::GetAnnotationRenderer("test2");
CPPUNIT_ASSERT_MESSAGE("Testing if new instance can be created by using different ID", ap1_test2 != ap1_test1);
mitk::LayoutAnnotationRenderer *ol1_test1 = mitk::LayoutAnnotationRenderer::GetAnnotationRenderer("test1");
CPPUNIT_ASSERT_MESSAGE("Testing availability of LayoutAnnotationRenderer service", ol1_test1);
mitk::LayoutAnnotationRenderer *ol2_test1 = mitk::LayoutAnnotationRenderer::GetAnnotationRenderer("test1");
- CPPUNIT_ASSERT_MESSAGE("Testing if LayoutAnnotationRenderer of same kind stays avaliable", ol2_test1 == ol1_test1);
+ CPPUNIT_ASSERT_MESSAGE("Testing if LayoutAnnotationRenderer of same kind stays available", ol2_test1 == ol1_test1);
mitk::LayoutAnnotationRenderer *ol1_test2 = mitk::LayoutAnnotationRenderer::GetAnnotationRenderer("test2");
CPPUNIT_ASSERT_MESSAGE("Testing if new instance can be created by using different ID", ol1_test2 != ol1_test1);
CPPUNIT_ASSERT_MESSAGE(
"Testing if LayoutAnnotationRenderer and ManualPlacementAnnotationRenderer services are different",
(mitk::AbstractAnnotationRenderer *)ol1_test1 != (mitk::AbstractAnnotationRenderer *)ap1_test1);
}
void AnnotationTest() {}
};
MITK_TEST_SUITE_REGISTRATION(mitkAnnotation)
diff --git a/Modules/AppUtil/include/mitkBaseApplication.h b/Modules/AppUtil/include/mitkBaseApplication.h
index c4fe0e9116..8e51fe1316 100644
--- a/Modules/AppUtil/include/mitkBaseApplication.h
+++ b/Modules/AppUtil/include/mitkBaseApplication.h
@@ -1,320 +1,320 @@
/*============================================================================
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 mitkBaseApplication_h
#define mitkBaseApplication_h
#include <MitkAppUtilExports.h>
#include <Poco/Util/Application.h>
#include <QString>
#include <QVariant>
class ctkPluginContext;
class ctkPluginFramework;
class QCoreApplication;
class QTranslator;
namespace mitk
{
/**
* A utility class for starting BlueBerry applications.
*
* In the simplest case, create an instance of this class and call run().
* This will launch a CTK plugin framework instance and execute the
* default application registered by a plug-in via the
* org.blueberry.osgi.applications extension point.
*
* This class contains many convenience methods to:
* - Put the application in <em>safe mode</em> which catches unhandled
* exceptions thrown in the Qt event loop and displays an error
* message.
* - Put the application in <em>single mode</em> which by default
* sends the command line arguments to an already running instance
* of the same application instead of creating a second instance.
* - Add a list of library names which should be pre-loaded at
* application start-up, e.g. to speed up the initial launch during
* the caching process of the plug-in meta-data.
* - Set a custom provisioning file to start a specific set of CTK
* plug-ins during application start-up.
* - Set and get CTK plugin framework properties
*
* The behavior can further be customized by deriving from BaseApplication
* and overriding specific methods, such as:
* - initializeLibraryPaths() to add specific library / plugin search paths
* - defineOptions(Poco::Util::OptionSet&) to define a custom set of
* command line options
* - getQApplication() to provide a custom QCoreApplication instance
*
* A simple but complete example:
* \code
* #include <mitkBaseApplication.h>
*
* int main(int argc, char* argv[])
* {
* mitk::BaseApplication app(argc, argv);
* app.setApplicationName("MyApp");
* app.setOrganizationName("MyOrganization");
*
* // Run the workbench
* return app.run();
* }
* \endcode
*/
class MITKAPPUTIL_EXPORT BaseApplication : public Poco::Util::Application
{
public:
// Command line arguments
static const QString ARG_APPLICATION;
static const QString ARG_CLEAN;
static const QString ARG_CONSOLELOG;
static const QString ARG_DEBUG;
static const QString ARG_FORCE_PLUGIN_INSTALL;
static const QString ARG_HOME;
static const QString ARG_NEWINSTANCE;
static const QString ARG_NO_LAZY_REGISTRY_CACHE_LOADING;
static const QString ARG_NO_REGISTRY_CACHE;
static const QString ARG_PLUGIN_CACHE;
static const QString ARG_PLUGIN_DIRS;
static const QString ARG_PRELOAD_LIBRARY;
static const QString ARG_PRODUCT;
static const QString ARG_PROVISIONING;
static const QString ARG_REGISTRY_MULTI_LANGUAGE;
static const QString ARG_SPLASH_IMAGE;
static const QString ARG_STORAGE_DIR;
static const QString ARG_XARGS;
static const QString ARG_LOG_QT_MESSAGES;
static const QString ARG_SEGMENTATION_LABELSET_PRESET;
static const QString ARG_SEGMENTATION_LABEL_SUGGESTIONS;
// BlueBerry specific plugin framework properties
static const QString PROP_APPLICATION;
static const QString PROP_FORCE_PLUGIN_INSTALL;
static const QString PROP_NEWINSTANCE;
static const QString PROP_NO_LAZY_REGISTRY_CACHE_LOADING;
static const QString PROP_NO_REGISTRY_CACHE;
static const QString PROP_PRODUCT;
static const QString PROP_REGISTRY_MULTI_LANGUAGE;
BaseApplication(int argc, char **argv);
~BaseApplication() override;
/**
* Initialize the Qt library such that a QCoreApplication
* instance is available and e.g. Qt widgets can be created.
*
* This is usually not called directly by the user.
*/
void initializeQt();
/**
* Launches the BlueBerry framework and runs the default application
* or the one specified in the PROP_APPLICATION framework property.
*
* @return The return code of the application after it was shut down.
*/
int run() override;
void printHelp(const std::string &name, const std::string &value);
/**
* Set the application name. Same as QCoreApplication::setApplicationName.
* @param name The application name.
*/
void setApplicationName(const QString &name);
QString getApplicationName() const;
/**
* Set the organization name. Same as QCoreApplication::setOrganizationName.
* @param name The organization name.
*/
void setOrganizationName(const QString &name);
QString getOrganizationName() const;
/**
* Set the organization domain. Same as QCoreApplication::setOrganizationDomain.
* @param name The organization domain.
*/
void setOrganizationDomain(const QString &name);
QString getOrganizationDomain() const;
/**
* Put the application in single mode, which by default only allows
* a single instance of the application to be created.
*
* Calling this method after run() has been called has no effect.
*
* @param singleMode
*/
void setSingleMode(bool singleMode);
bool getSingleMode() const;
/**
* Put the application in safe mode, catching exceptions from the
* Qt event loop.
*
* @param safeMode
*/
void setSafeMode(bool safeMode);
bool getSafeMode() const;
/**
- * Set a list of library names or absoulte file paths
+ * Set a list of library names or absolute file paths
* which should be loaded at application start-up. The name
* and file path may contain a library version appended at the
* end and separated by a '$' charactger.
*
* For example <code>liborg_mitk_gui_qt_common$1.0</code>.
* Platform specific suffixes are appended automatically.
*
* @param libraryBaseNames A list of library base names.
*/
void setPreloadLibraries(const QStringList &libraryBaseNames);
/**
* Get the list of library base names which should be pre-loaded.
*
* @return A list of pre-loaded libraries.
*/
QStringList getPreloadLibraries() const;
/**
* Set the path to the provisioning file.
*
* By default a provisioning file located in the same directory
* as the executable and named \<executable\>.provisioning
* is loaded if it exists. To disable parsing of provisioning
* files, use an empty string as the argument. Use a
* null QString (\c QString::null ) to reset to the
* default behaviour.
*
* @param filePath An absolute file path to the provisioning file.
*/
void setProvisioningFilePath(const QString &filePath);
/**
* Get the file path to the provisioning file.
* @return The provisioning file path.
*/
QString getProvisioningFilePath() const;
void setProperty(const QString &property, const QVariant &value);
QVariant getProperty(const QString &property) const;
void installTranslator(QTranslator*);
bool isRunning();
void sendMessage(const QByteArray);
protected:
void initialize(Poco::Util::Application &self) override;
void uninitialize() override;
int getArgc() const;
char **getArgv() const;
/**
* Get the framework storage directory for the CTK plugin
* framework. This method is called in the initialize(Poco::Util::Application&)
* method. It must not be called without a QCoreApplications instance.
*
* @return The CTK Plugin Framework storage directory.
*/
virtual QString getCTKFrameworkStorageDir() const;
/**
* Initialize the CppMicroServices library.
*
* The default implementation set the CppMicroServices storage
* path to the current ctkPluginConstants::FRAMEWORK_STORAGE property
* value.
*
* This method is called in the initialize(Poco::Util::Application&)
* after the CTK Plugin Framework storage directory property
* was set.
*/
virtual void initializeCppMicroServices();
/**
* Get the QCoreApplication object.
*
* This method is called in the initialize(Poco::Util::Application&)
* method and must create a QCoreApplication instance if the
* global qApp variable is not initialized yet.
*
* @return The current QCoreApplication instance. This method
* never returns null.
*/
virtual QCoreApplication *getQApplication() const;
/**
* Add plugin library search paths to the CTK Plugin Framework.
*
* This method is called in the nitialize(Poco::Util::Application&)
* method after getQApplication() was called.
*/
virtual void initializeLibraryPaths();
/**
* Runs the application for which the platform was started. The platform
* must be running.
* <p>
* The given argument is passed to the application being run. If it is an invalid QVariant
* then the command line arguments used in starting the platform, and not consumed
* by the platform code, are passed to the application as a <code>QStringList</code>.
* </p>
* @param args the argument passed to the application. May be <code>invalid</code>
* @return the result of running the application
* @throws std::exception if anything goes wrong
*/
int main(const std::vector<std::string> &args) override;
/**
* Define command line arguments
* @param options
*/
void defineOptions(Poco::Util::OptionSet &options) override;
QSharedPointer<ctkPluginFramework> getFramework() const;
ctkPluginContext *getFrameworkContext() const;
/**
* Get the initial properties for the CTK plugin framework.
*
* The returned map contains the initial framework properties for
* initializing the CTK plugin framework. The value of specific
* properties may change at runtime and differ from the initial
* value.
*
* @return The initial CTK Plugin Framework properties.
*/
QHash<QString, QVariant> getFrameworkProperties() const;
/*
* Initialize and display the splash screen if an image filename is given
*
*/
void initializeSplashScreen(QCoreApplication * application) const;
private:
struct Impl;
Impl* d;
};
}
#endif
diff --git a/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox b/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox
index c78a2e14d3..681be03b72 100644
--- a/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox
+++ b/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox
@@ -1,45 +1,45 @@
/**
\page mitkBasicImageProcessingMiniAppsPortalPage MITK Basic Image Processing Mini Apps
\tableofcontents
-The Basic Image Processing Mini Apps bundle the functionality that is commonly neeeded for the processing of medical images. As all other MiniApps, they follow the <a href="https://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation">Slicer Execution Model</a> in describing themselves via xml. You can simply obtain a description by calling the MiniApp without any parameter. If the MiniApp is calles with the option "--xml" a XML description of all possible parameter is added.
+The Basic Image Processing Mini Apps bundle the functionality that is commonly needed for the processing of medical images. As all other MiniApps, they follow the <a href="https://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation">Slicer Execution Model</a> in describing themselves via xml. You can simply obtain a description by calling the MiniApp without any parameter. If the MiniApp is calles with the option "--xml" a XML description of all possible parameter is added.
\section bipmasec1 Description of Mini Apps
\subsection bipmasub1 mitkFileConverter
Allows to convert a file from one type to another file type, for example to convert an image, saved in the nifti format to an image saved in the .nrrd format.
\subsection bipmasub2 mitkForwardWavelet
Calculates the forward wavelet transformation of an image. The output will consist of multiple images, which will be saved in the format \<output\>%id%\<output-extension\>, where \<output\> and \<output-extension\> are specified by the user and %id% is a consecutive number.
\subsection bipmasub3 mitkImageAndValueArithmetic
Mathematical operations with two operants, the individual voxels of the image and a specified floating point value. By default, the floating point value is the right operand.
\subsection bipmasub4 mitkImageTypeCovnerter
-Convert the data fromat that is used to save a voxel of an image.
+Convert the data format that is used to save a voxel of an image.
\subsection bipmasub5 mitkLaplacianOfGaussian
Calculate the Laplacian of Gaussian of an image with the specified sigma value.
\subsection bipmasub6 mitkMaskOutlierFiltering
Can be used to clean an segmentation. The mean and standard deviation of the intensities which are masked is calculated and then all mask voxels are removed that cover image voxels which are not within a 3 sigma range.
\subsection bipmasub7 mitkMaskRangeBasedFiltering
Removing all voxels from a mask that cover image voxels which are outside of a given range. The range can be either specified by a lower limit, a upper limit, or both at the same time.
\subsection bipmasub8 mitkMultiResolutionPyramid
Calculate a Multi-Resolution Pyramid of the given image. The resolution is reduced by factor 2 for each step.
\subsection bipmasub9 mitkResampleImage
Resample a mask to a new spacing
\subsection bipmasub10 mitkResampleMask
Similar to mitkResampleImage, but specificly tailored to resampling a mask.
\subsection bipmasub11 mitkSingleImageArithmetic
Applies single operand mathematical operations to each voxel of an image
\subsection bipmasub12 mitkTwoImageArithmetic
Applied two operand mathematical operations to each voxels of two images.
*/
diff --git a/Modules/BoundingShape/include/mitkBoundingShapeCropper.h b/Modules/BoundingShape/include/mitkBoundingShapeCropper.h
index 695b52ab03..c99a4c4843 100644
--- a/Modules/BoundingShape/include/mitkBoundingShapeCropper.h
+++ b/Modules/BoundingShape/include/mitkBoundingShapeCropper.h
@@ -1,145 +1,145 @@
/*============================================================================
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 mitkBoundingShapeCropper_h
#define mitkBoundingShapeCropper_h
#include "MitkBoundingShapeExports.h"
#include "mitkBoundingShapeCropper.h"
#include "mitkCommon.h"
#include "mitkGeometryData.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
#include "itkImage.h"
namespace mitk
{
/** Documentation
* @brief Crops or masks an Boundingbox defined by GeometryData out of an mitk Image
*
* Input Parameters are a mitk::GeometryData and an mitk::Image
* Masking: Pixel on the outside of the bounding box will have a pixelvalue of m_OutsideValue
* Cropping: Output image has the same size as the bounding box
*/
//## @ingroup Process
class MITKBOUNDINGSHAPE_EXPORT BoundingShapeCropper : public ImageToImageFilter
{
public:
mitkClassMacro(BoundingShapeCropper, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* @brief Set geometry of the bounding object
*/
void SetGeometry(const mitk::GeometryData *geometry);
/**
* @brief Get geometry of the bounding object
*/
// const mitk::GeometryData* GetGeometryData() const;
/**
* @brief Sets and Gets the outside value for masking
*/
itkSetMacro(OutsideValue, ScalarType);
itkGetMacro(OutsideValue, ScalarType);
/**
* @brief Sets and Gets whether a masking or cropping needs to be performed
*/
itkSetMacro(UseWholeInputRegion, bool);
itkGetMacro(UseWholeInputRegion, bool);
/**
- * @brief Sets and Gets the current timestep for images with 4 dimensons
+ * @brief Sets and Gets the current timestep for images with 4 dimensions
*/
itkSetMacro(CurrentTimeStep, ScalarType);
itkGetMacro(CurrentTimeStep, ScalarType);
/**
*@brief Sets and Gets whether only one timestep is cropped / masked
*/
itkSetMacro(UseCropTimeStepOnly, bool);
itkGetMacro(UseCropTimeStepOnly, bool);
protected:
BoundingShapeCropper();
~BoundingShapeCropper() override;
virtual const PixelType GetOutputPixelType();
/**
* @brief Reimplemented from ImageToImageFilter
*/
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
/**
* @brief Template Function for cropping and masking images with scalar pixel type
*/
template <typename TPixel, unsigned int VImageDimension>
void CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, int timeStep);
/**
*@brief Process the image and create the output
**/
virtual void ComputeData(mitk::Image *input3D, int boTimeStep);
// virtual void ComputeData(mitk::LabelSetImage* image, int boTimeStep);
private:
/**
*@brief GeometryData Type to capsulate all necessary components of the bounding object
**/
mitk::GeometryData::Pointer m_Geometry;
/**
* @brief scalar value for outside pixels (default: 0)
*/
ScalarType m_OutsideValue;
/**
* @brief Use m_UseCropTimeStepOnly for only cropping a single time step(default: \a false)
*/
bool m_UseCropTimeStepOnly;
/**
* @brief Current time step displayed
*/
int m_CurrentTimeStep;
/**
* @brief Use m_UseWholeInputRegion for deciding whether a cropping or masking will be performed
*/
bool m_UseWholeInputRegion;
/**
* @brief Select single input image in a timeseries
*/
mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
/**
* @brief Select single output image in a timeseries
*/
mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
/**
* @brief Region of input needed for cutting
*/
typedef itk::ImageRegion<5> RegionType;
mitk::SlicedData::RegionType m_InputRequestedRegion;
/**
* @brief Time when Header was last initialized
**/
itk::TimeStamp m_TimeOfHeaderInitialization;
};
} // namespace mitk
#endif
diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp
index d092bb6d5d..9ccb154549 100644
--- a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp
+++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp
@@ -1,346 +1,346 @@
/*============================================================================
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 "mitkBoundingShapeCropper.h"
#include "mitkGeometry3D.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageToItk.h"
#include "mitkStatusBar.h"
#include "mitkTimeHelper.h"
#include <cmath>
#include "vtkMatrix4x4.h"
#include "vtkSmartPointer.h"
#include "vtkTransform.h"
#include "itkImageRegionIteratorWithIndex.h"
#include <itkImageIOBase.h>
#include <itkImageRegionConstIterator.h>
#include <itkRGBAPixel.h>
#include <itkRGBPixel.h>
namespace mitk
{
BoundingShapeCropper::BoundingShapeCropper()
: m_Geometry(nullptr),
m_OutsideValue(0),
m_UseCropTimeStepOnly(false),
m_CurrentTimeStep(0),
m_UseWholeInputRegion(false),
m_InputTimeSelector(mitk::ImageTimeSelector::New()),
m_OutputTimeSelector(mitk::ImageTimeSelector::New())
{
this->SetNumberOfIndexedInputs(2);
this->SetNumberOfRequiredInputs(2);
}
BoundingShapeCropper::~BoundingShapeCropper() {}
template <typename TPixel, unsigned int VImageDimension>
void BoundingShapeCropper::CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, int timeStep)
{
MITK_INFO << "Scalar Pixeltype" << std::endl;
typedef TPixel TOutputPixel;
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<TOutputPixel, VImageDimension> ItkOutputImageType;
typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
TOutputPixel outsideValue = this->GetOutsideValue();
// currently 0 if not set in advance
// TODO: change default value to itk::NumericTraits<TOutputPixel>::min();
if (this->m_Geometry.IsNull())
return;
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
std::cout << " image is nullptr...returning" << std::endl;
return;
}
// first convert the index
typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
itk2vtk(this->m_InputRequestedRegion.GetIndex(), tmpIndex);
typename ItkRegionType::IndexType index;
index.SetIndex(tmpIndex);
// then convert the size
typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
itk2vtk(this->m_InputRequestedRegion.GetSize(), tmpSize);
typename ItkRegionType::SizeType size;
size.SetSize(tmpSize);
// create the ITK-image-region out of index and size
ItkRegionType inputRegionOfInterest(index, size);
// Get access to the MITK output image via an ITK image
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(this->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// create the iterators
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
// Cut the boundingbox out of the image by iterating through all images
// TODO: use more efficient method by using the contour instead off all single pixels
mitk::Point3D p;
mitk::BaseGeometry *inputGeometry = this->GetInput()->GetGeometry(timeStep);
// calculates translation based on offset+extent not on the transformation matrix
// NOTE: center of the box is
vtkSmartPointer<vtkMatrix4x4> imageTransform = this->m_Geometry->GetGeometry()->GetVtkTransform()->GetMatrix();
Point3D center = this->m_Geometry->GetGeometry()->GetCenter();
auto translation = vtkSmartPointer<vtkTransform>::New();
translation->Translate(center[0] - imageTransform->GetElement(0, 3),
center[1] - imageTransform->GetElement(1, 3),
center[2] - imageTransform->GetElement(2, 3));
auto transform = vtkSmartPointer<vtkTransform>::New();
transform->SetMatrix(imageTransform);
transform->PostMultiply();
transform->Concatenate(translation);
transform->Update();
mitk::Vector3D extent;
for (unsigned int i = 0; i < 3; ++i)
extent[i] = (this->m_Geometry->GetGeometry()->GetExtent(i));
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
ScalarType p2[4];
p2[0] = p[0];
p2[1] = p[1];
p2[2] = p[2];
p2[3] = 1;
// transform point from world to object coordinates
transform->GetInverse()->TransformPoint(p2, p2);
// check if the world point is within bounds
bool isInside = (p2[0] >= (-extent[0] / 2.0)) && (p2[0] <= (extent[0] / 2.0)) && (p2[1] >= (-extent[1] / 2.0)) &&
(p2[1] <= (extent[1] / 2.0)) && (p2[2] >= (-extent[2] / 2.0)) && (p2[2] <= (extent[2] / 2.0));
if ((!this->m_UseCropTimeStepOnly && isInside) ||
(this->m_UseCropTimeStepOnly && timeStep == this->m_CurrentTimeStep && isInside))
{
outputIt.Set((TOutputPixel)inputIt.Value());
}
else
{
outputIt.Set(outsideValue);
}
}
}
void BoundingShapeCropper::SetGeometry(const mitk::GeometryData *geometry)
{
m_Geometry = const_cast<mitk::GeometryData *>(geometry);
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput(1, const_cast<mitk::GeometryData *>(geometry));
}
// const mitk::GeometryData* BoundingShapeCropper::GetGeometryData() const
//{
// return m_Geometry.GetPointer();
//}
const mitk::PixelType BoundingShapeCropper::GetOutputPixelType() { return this->GetInput()->GetPixelType(); }
void BoundingShapeCropper::GenerateInputRequestedRegion()
{
mitk::Image *output = this->GetOutput();
if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) ||
(m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
return;
GenerateTimeInInputRegion(output, this->GetInput());
}
void BoundingShapeCropper::GenerateOutputInformation()
{
// Set Cropping region
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
return;
mitk::Image::Pointer input = this->GetInput();
if (input.IsNull())
{
mitkThrow() << "Input is not a mitk::Image";
}
itkDebugMacro(<< "GenerateOutputInformation()");
unsigned int dimension = input->GetDimension();
if (dimension < 3)
{
mitkThrow() << "ImageCropper cannot handle 1D or 2D Objects.";
}
if ((m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
return;
mitk::BaseGeometry *bsGeometry = m_Geometry->GetGeometry();
mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry();
// calculate bounding box
mitk::BoundingBox::Pointer bsBoxRelativeToImage =
bsGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform());
// pre-initialize input-requested-region to largest-possible-region
m_InputRequestedRegion = input->GetLargestPossibleRegion();
// build region out of bounding-box of index and size of the bounding box
mitk::SlicedData::IndexType index = m_InputRequestedRegion.GetIndex(); // init times and channels
mitk::BoundingBox::PointType min = bsBoxRelativeToImage->GetMinimum();
mitk::SlicedData::SizeType size = m_InputRequestedRegion.GetSize(); // init times and channels
mitk::BoundingBox::PointType max = bsBoxRelativeToImage->GetMaximum();
mitk::Point<BoundingBox::PointType::CoordRepType, 5> maxCorrected;
mitk::Point<BoundingBox::PointType::CoordRepType, 5> minCorrected;
for (unsigned int i = 0; i < 3; i++)
{
maxCorrected[i] = max[i];
minCorrected[i] = min[i];
}
maxCorrected[3] = input->GetDimensions()[3];
maxCorrected[4] = 0;
minCorrected[3] = 0;
minCorrected[4] = 0;
for (unsigned int i = 0; i < dimension; i++)
{
index[i] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(minCorrected[i]));
size[i] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(maxCorrected[i]) - index[i]);
}
mitk::SlicedData::RegionType bsRegion(index, size);
if (m_UseWholeInputRegion == false)
{
// crop input-requested-region with region of bounding-object
if (m_InputRequestedRegion.Crop(bsRegion) == false)
{
// crop not possible => do nothing: set time size to 0.
size.Fill(0);
m_InputRequestedRegion.SetSize(size);
bsRegion.SetSize(size);
mitkThrow() << "No overlap of the image and the cropping object.";
}
}
// initialize output image
auto dimensions = new unsigned int[dimension];
if (dimension > 3 && !this->GetUseCropTimeStepOnly())
memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int));
else
dimension = 3; // set timeStep to zero if GetUseCropTimeStepOnly is true
itk2vtk(m_InputRequestedRegion.GetSize(), dimensions);
output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions);
delete[] dimensions;
// Apply transform of the input image to the new generated output image
mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion();
m_TimeOfHeaderInitialization.Modified();
}
void BoundingShapeCropper::ComputeData(mitk::Image *image, int boTimeStep)
{
// examine dimension and pixeltype
if ((image == nullptr) || (image->GetDimension() > 4) || (image->GetDimension() <= 2))
{
MITK_ERROR << "Filter cannot handle dimensions less than 2 and greater than 4" << std::endl;
itkExceptionMacro("Filter cannot handle dimensions less than 2 and greater than 4");
return;
}
AccessByItk_1(image, CutImage, boTimeStep);
}
void BoundingShapeCropper::GenerateData()
{
MITK_INFO << "Generate Data" << std::endl;
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if (input.IsNull())
return;
if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) ||
(m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
return;
m_InputTimeSelector->SetInput(input);
m_OutputTimeSelector->SetInput(this->GetOutput());
mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion();
mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry();
- // iterate over all time steps and perform cropping or masking on all or a specific timestep (perviously specified
+ // iterate over all time steps and perform cropping or masking on all or a specific timestep (previously specified
// by UseCurrentTimeStepOnly)
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
if (this->m_UseCropTimeStepOnly)
{
mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(tstart);
auto indexToWorldTransform = AffineTransform3D::New();
indexToWorldTransform->SetParameters(input->GetSlicedGeometry(tstart)->GetIndexToWorldTransform()->GetParameters());
slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform);
const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
mitk::Point3D origin;
vtk2itk(start, origin);
inputImageGeometry->IndexToWorld(origin, origin);
slicedGeometry->SetOrigin(origin);
m_InputTimeSelector->SetTimeNr(m_CurrentTimeStep);
m_InputTimeSelector->UpdateLargestPossibleRegion();
m_OutputTimeSelector->SetTimeNr(tstart);
m_OutputTimeSelector->UpdateLargestPossibleRegion();
ComputeData(m_InputTimeSelector->GetOutput(), m_CurrentTimeStep);
}
else
{
int t;
for (t = tstart; t < tmax; ++t)
{
mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(t);
auto indexToWorldTransform = AffineTransform3D::New();
indexToWorldTransform->SetParameters(input->GetSlicedGeometry(t)->GetIndexToWorldTransform()->GetParameters());
slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform);
const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
mitk::Point3D origin;
vtk2itk(start, origin);
inputImageGeometry->IndexToWorld(origin, origin);
slicedGeometry->SetOrigin(origin);
m_InputTimeSelector->SetTimeNr(t);
m_InputTimeSelector->UpdateLargestPossibleRegion();
m_OutputTimeSelector->SetTimeNr(t);
m_OutputTimeSelector->UpdateLargestPossibleRegion();
ComputeData(m_InputTimeSelector->GetOutput(), t);
}
}
m_InputTimeSelector->SetInput(nullptr);
m_OutputTimeSelector->SetInput(nullptr);
m_TimeOfHeaderInitialization.Modified();
}
} // of namespace mitk
diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
index 27a7210bb2..5ce2869177 100644
--- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
+++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
@@ -1,605 +1,605 @@
/*============================================================================
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 "../DataManagement/mitkBoundingShapeUtil.h"
#include <mitkBoundingShapeInteractor.h>
#include <mitkDisplayActionEventBroadcast.h>
#include <mitkInteractionConst.h>
#include <mitkInteractionEventObserver.h>
#include <mitkInteractionKeyEvent.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkMouseWheelEvent.h>
#include <vtkCamera.h>
#include <vtkInteractorObserver.h>
#include <vtkInteractorStyle.h>
#include <vtkPointData.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include "usGetModuleContext.h"
#include "usModuleRegistry.h"
// Properties to allow the user to interact with the base data
const char *selectedColorPropertyName = "Bounding Shape.Selected Color";
const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color";
const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID";
const char *boundingShapePropertyName = "Bounding Shape";
namespace mitk
{
itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent);
class BoundingShapeInteractor::Impl
{
public:
Impl() : OriginalInteractionEnabled(false), RotationEnabled(false)
{
Point3D initialPoint;
initialPoint.Fill(0.0);
for (int i = 0; i < 6; ++i)
Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i)));
}
~Impl() {}
bool OriginalInteractionEnabled;
Point3D InitialPickedWorldPoint;
Point3D LastPickedWorldPoint;
Point2D InitialPickedDisplayPoint;
std::vector<Handle> Handles;
Handle ActiveHandle;
Geometry3D::Pointer OriginalGeometry;
bool RotationEnabled;
std::map<us::ServiceReferenceU, mitk::EventConfig> DisplayInteractionConfigs;
};
}
mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl)
{
}
mitk::BoundingShapeInteractor::~BoundingShapeInteractor()
{
this->RestoreNodeProperties();
delete m_Impl;
}
void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions()
{
// **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually
// executing an action
CONNECT_CONDITION("isHoveringOverObject", CheckOverObject);
CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles);
// **Function** in the statemachine patterns also referred to as **Actions**
CONNECT_FUNCTION("selectObject", SelectObject);
CONNECT_FUNCTION("deselectObject", DeselectObject);
CONNECT_FUNCTION("deselectHandles", DeselectHandles);
CONNECT_FUNCTION("initInteraction", InitInteraction);
CONNECT_FUNCTION("translateObject", TranslateObject);
CONNECT_FUNCTION("selectHandle", SelectHandle);
CONNECT_FUNCTION("scaleObject", ScaleObject);
// CONNECT_FUNCTION("rotateObject",RotateObject);
}
// RotateObject(StateMachineAction*, InteractionEvent* interactionEvent)
// void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry*
// geometry)
//{
// mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis);
// float pointX = 0.0f;
// float pointY = 0.0f;
// float pointZ = 0.0f;
// mitk::Point3D pointOfRotation;
// pointOfRotation.Fill(0.0);
// this->GetDataNode()->GetFloatProperty(anchorPointX, pointX);
// this->GetDataNode()->GetFloatProperty(anchorPointY, pointY);
// this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ);
// pointOfRotation[0] = pointX;
// pointOfRotation[1] = pointY;
// pointOfRotation[2] = pointZ;
//
// mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle);
//
// geometry->ExecuteOperation(doOp);
// delete doOp;
//}
void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled)
{
m_Impl->RotationEnabled = rotationEnabled;
}
void mitk::BoundingShapeInteractor::DataNodeChanged()
{
mitk::DataNode::Pointer newInputNode = this->GetDataNode();
if (newInputNode == nullptr)
return;
// add color properties
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(selectedColorPropertyName));
mitk::ColorProperty::Pointer deselectedColor =
dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName));
if (selectedColor.IsNull())
newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0));
if (deselectedColor.IsNull())
newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 0.0, 0.0));
newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true));
newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1));
newInputNode->SetProperty("layer", mitk::IntProperty::New(101));
newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true));
newInputNode->SetBoolProperty("pickable", true);
mitk::ColorProperty::Pointer initialColor =
dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName));
if (initialColor.IsNotNull())
{
newInputNode->SetColor(initialColor->GetColor());
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D &center)
{
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep);
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
if (m_Impl->Handles.size() == 6)
{
// set handle positions
Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]);
Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]);
Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]);
Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]);
Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]);
Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]);
m_Impl->Handles[0].SetPosition(pointLeft);
m_Impl->Handles[1].SetPosition(pointRight);
m_Impl->Handles[2].SetPosition(pointTop);
m_Impl->Handles[3].SetPosition(pointBottom);
m_Impl->Handles[4].SetPosition(pointFront);
m_Impl->Handles[5].SetPosition(pointBack);
// calculate center based on half way of the distance between two opposing cornerpoints
center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
}
}
void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node)
{
this->RestoreNodeProperties(); // if there is another node set, restore it's color
if (node == nullptr)
return;
DataInteractor::SetDataNode(node); // calls DataNodeChanged internally
this->DataNodeChanged();
}
bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep);
// calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the
// center not in the origin)
vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix();
Point3D center = geometry->GetCenter();
auto translation = vtkSmartPointer<vtkTransform>::New();
auto transform = vtkSmartPointer<vtkTransform>::New();
translation->Translate(center[0] - imageTransform->GetElement(0, 3),
center[1] - imageTransform->GetElement(1, 3),
center[2] - imageTransform->GetElement(2, 3));
transform->SetMatrix(imageTransform);
transform->PostMultiply();
transform->Concatenate(translation);
transform->Update();
mitk::Vector3D extent;
for (unsigned int i = 0; i < 3; ++i)
extent[i] = (geometry->GetExtent(i));
Point3D currentWorldPosition;
Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen();
interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition);
ScalarType transformedPosition[4];
transformedPosition[0] = currentWorldPosition[0];
transformedPosition[1] = currentWorldPosition[1];
transformedPosition[2] = currentWorldPosition[2];
transformedPosition[3] = 1;
// transform point from world to object coordinates
transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition);
// check if the world point is within bounds
bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) &&
(transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) &&
(transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0));
return isInside;
}
bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent)
{
Point3D boundingBoxCenter;
HandlePositionChanged(interactionEvent, boundingBoxCenter);
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
Point2D displayCenterPoint;
// to do: change to actual time step (currently not necessary because geometry remains the same for each timestep
int timeStep = 0;
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep);
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint);
double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM
mitk::DoubleProperty::Pointer handleSizeProperty =
dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor"));
ScalarType initialHandleSize;
if (handleSizeProperty != nullptr)
initialHandleSize = handleSizeProperty->GetValue();
else
initialHandleSize = 1.0 / 40.0;
mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM();
ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize;
unsigned int handleNum = 0;
for (auto &handle : m_Impl->Handles)
{
Point2D centerpoint;
interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint);
Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen();
if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) &&
(currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) >
(handlesize / scale))) // check if mouse is hovering over center point
{
handle.SetActive(true);
m_Impl->ActiveHandle = handle;
this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName,
mitk::IntProperty::New(handleNum++));
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return true;
}
else
{
handleNum++;
handle.SetActive(false);
}
this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1));
}
return false;
}
void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *)
{
this->DisableOriginalInteraction();
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName));
if (selectedColor.IsNotNull())
{
this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor);
}
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *)
{
this->DisableOriginalInteraction();
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1));
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *)
{
- this->DisableOriginalInteraction(); // disable crosshair interaction and scolling if user is hovering over the object
+ this->DisableOriginalInteraction(); // disable crosshair interaction and scrolling if user is hovering over the object
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer selectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(selectedColorPropertyName));
if (selectedColor.IsNotNull())
{
node->GetPropertyList()->SetProperty("color", selectedColor);
}
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *)
{
- this->EnableOriginalInteraction(); // enable crosshair interaction and scolling if user is hovering over the object
+ this->EnableOriginalInteraction(); // enable crosshair interaction and scrolling if user is hovering over the object
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
mitk::ColorProperty::Pointer deselectedColor =
dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName));
if (deselectedColor.IsNotNull())
{
node->GetPropertyList()->SetProperty("color", deselectedColor);
}
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent)
{
InitMembers(interactionEvent);
}
bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
// get initial position coordinates
m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld();
m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld();
return true;
}
void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry =
this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep);
Vector3D spacing = geometry->GetSpacing();
Point3D currentPickedPoint;
interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint);
Vector3D interactionMove;
// pixel aligned shifting of the bounding box
interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0];
interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1];
interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2];
if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) !=
- 0.0) // only update current position if a movement occured
+ 0.0) // only update current position if a movement occurred
{
m_Impl->LastPickedWorldPoint = currentPickedPoint;
geometry->SetOrigin(geometry->GetOrigin() + interactionMove);
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
return;
}
void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData());
Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition();
Point3D currentPickedPoint;
interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint);
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep);
Vector3D spacing = geometry->GetSpacing();
// pixel aligned bounding box
Vector3D interactionMove;
interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]);
interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]);
interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]);
std::vector<int> faces = m_Impl->ActiveHandle.GetFaceIndices();
auto pointscontainer = mitk::BoundingBox::PointsContainer::New();
// calculate cornerpoints from geometry plus visualization offset
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
unsigned int num = 0;
for (const auto &point : cornerPoints)
{
pointscontainer->InsertElement(num++, point);
}
// calculate center based on half way of the distance between two opposing cornerpoints
mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
Vector3D faceNormal;
faceNormal[0] = handlePickedPoint[0] - center[0];
faceNormal[1] = handlePickedPoint[1] - center[1];
faceNormal[2] = handlePickedPoint[2] - center[2];
Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal;
// calculate cornerpoints from geometry without visualization offset to update actual geometry
cornerPoints = GetCornerPoints(geometry, false);
num = 0;
for (const auto &point : cornerPoints)
{
pointscontainer->InsertElement(num++, point);
}
bool positionChangeThreshold = true;
for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points
{
if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3]))
{
Point3D point = pointscontainer->GetElement(numFaces);
if (m_Impl->RotationEnabled) // apply if geometry is rotated and a pixel aligned shift is not possible
{
point[0] += faceShift[0];
point[1] += faceShift[1];
point[2] += faceShift[2];
}
else // shift pixelwise
{
point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0];
point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1];
point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2];
}
if (point == pointscontainer->GetElement(numFaces))
positionChangeThreshold = false;
else
m_Impl->LastPickedWorldPoint = point;
pointscontainer->InsertElement(numFaces, point);
}
}
if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel
{
auto inverse = mitk::AffineTransform3D::New();
geometry->GetIndexToWorldTransform()->GetInverse(inverse);
for (unsigned int pointid = 0; pointid < 8; pointid++)
{
pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid)));
}
auto bbox = mitk::BoundingBox::New();
bbox->SetPoints(pointscontainer);
bbox->ComputeBoundingBox();
mitk::Point3D BBmin = bbox->GetMinimum();
mitk::Point3D BBmax = bbox->GetMaximum();
if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 &&
std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero
{
geometry->SetBounds(bbox->GetBounds());
geometry->Modified();
this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date
this->GetDataNode()->GetData()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
return;
}
void mitk::BoundingShapeInteractor::RestoreNodeProperties()
{
mitk::DataNode::Pointer inputNode = this->GetDataNode();
if (inputNode.IsNull())
return;
mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0));
if (color.IsNotNull())
{
inputNode->GetPropertyList()->SetProperty("color", color);
}
inputNode->SetProperty("layer", mitk::IntProperty::New(99));
inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false));
inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName);
EnableOriginalInteraction();
// update rendering
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::BoundingShapeInteractor::EnableOriginalInteraction()
{
// Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools
// in new interaction framework
for (const auto& displayInteractionConfig : m_Impl->DisplayInteractionConfigs)
{
if (displayInteractionConfig.first)
{
auto displayActionEventBroadcast = static_cast<mitk::DisplayActionEventBroadcast *>(
us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(displayInteractionConfig.first));
if (nullptr != displayActionEventBroadcast)
{
// here the regular configuration is loaded again
displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second);
}
}
}
m_Impl->DisplayInteractionConfigs.clear();
m_Impl->OriginalInteractionEnabled = true;
}
void mitk::BoundingShapeInteractor::DisableOriginalInteraction()
{
// dont deactivate twice, else we will clutter the config list ...
if (false == m_Impl->OriginalInteractionEnabled)
return;
// As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts
// with tools
// Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction
// will still be enabled
m_Impl->DisplayInteractionConfigs.clear();
auto eventObservers = us::GetModuleContext()->GetServiceReferences<mitk::InteractionEventObserver>();
for (const auto& eventObserver : eventObservers)
{
auto *displayActionEventBroadcast = dynamic_cast<mitk::DisplayActionEventBroadcast *>(
us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(eventObserver));
if (nullptr != displayActionEventBroadcast)
{
// remember the original configuration
m_Impl->DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig()));
// here the alternative configuration is loaded
displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml");
}
}
m_Impl->OriginalInteractionEnabled = false;
}
diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp
index 5114a48201..c57fb18949 100644
--- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp
+++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp
@@ -1,462 +1,462 @@
/*============================================================================
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 "../DataManagement/mitkBoundingShapeUtil.h"
#include <mitkBaseProperty.h>
#include <mitkBoundingShapeVtkMapper2D.h>
#include <vtkActor2D.h>
#include <vtkAppendPolyData.h>
#include <vtkCoordinate.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper2D.h>
#include <vtkProperty2D.h>
#include <vtkStripper.h>
#include <vtkTransformFilter.h>
#include <vtkTransformPolyDataFilter.h>
namespace mitk
{
class BoundingShapeVtkMapper2D::Impl
{
public:
Impl()
{
Point3D initialPoint;
initialPoint.Fill(0);
for (int i = 0; i < 6; ++i)
HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i)));
}
std::vector<Handle> HandlePropertyList;
mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler;
};
}
mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage()
: m_Actor(vtkSmartPointer<vtkActor>::New()),
m_HandleActor(vtkSmartPointer<vtkActor2D>::New()),
m_SelectedHandleActor(vtkSmartPointer<vtkActor2D>::New()),
m_Mapper(vtkSmartPointer<vtkPolyDataMapper>::New()),
m_HandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()),
m_SelectedHandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()),
m_Cutter(vtkSmartPointer<vtkCutter>::New()),
m_CuttingPlane(vtkSmartPointer<vtkPlane>::New()),
m_LastSliceNumber(0),
m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()),
m_ZoomFactor(1.0)
{
m_Actor->SetMapper(m_Mapper);
m_Actor->VisibilityOn();
m_HandleActor->SetMapper(m_HandleMapper);
m_HandleActor->VisibilityOn();
m_SelectedHandleActor->VisibilityOn();
m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0);
m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper);
vtkCoordinate *tcoord = vtkCoordinate::New();
tcoord->SetCoordinateSystemToWorld();
m_SelectedHandleMapper->SetTransformCoordinate(tcoord);
tcoord->Delete();
m_Cutter->SetCutFunction(m_CuttingPlane);
for (int i = 0; i < 6; ++i)
m_Handles.push_back(vtkSmartPointer<vtkCubeSource>::New());
m_PropAssembly->AddPart(m_Actor);
m_PropAssembly->AddPart(m_HandleActor);
m_PropAssembly->VisibilityOn();
}
bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired(mitk::BaseRenderer *renderer,
mitk::Mapper *mapper,
mitk::DataNode *dataNode)
{
const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (m_LastGenerateDataTime < worldGeometry->GetMTime())
return true;
unsigned int sliceNumber = renderer->GetSlice();
if (m_LastSliceNumber != sliceNumber)
return true;
if (mapper && m_LastGenerateDataTime < mapper->GetMTime())
return true;
if (dataNode)
{
if (m_LastGenerateDataTime < dataNode->GetMTime())
return true;
mitk::BaseData *data = dataNode->GetData();
if (data && m_LastGenerateDataTime < data->GetMTime())
return true;
}
return false;
}
mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage()
{
}
void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
this->GenerateDataForRenderer(renderer);
}
void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite)
{
Superclass::SetDefaultProperties(node, renderer, overwrite);
node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite);
}
mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl)
{
}
mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D()
{
delete m_Impl;
}
void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *renderer)
{
const DataNode::Pointer node = GetDataNode();
if (node == nullptr)
return;
LocalStorage *localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer);
// either update if GeometryData was modified or if the zooming was performed
bool needGenerateData = localStorage->IsUpdateRequired(
renderer, this, GetDataNode()); // true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() ||
// localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime();
// //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode());
double scale = renderer->GetScaleFactorMMPerDisplayUnit();
if (std::abs(scale - localStorage->m_ZoomFactor) > 0.001)
{
localStorage->m_ZoomFactor = scale;
needGenerateData = true;
}
if (needGenerateData)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
{
localStorage->m_Actor->VisibilityOff();
return;
}
GeometryData::Pointer shape = static_cast<GeometryData *>(node->GetData());
if (shape == nullptr)
return;
mitk::BaseGeometry::Pointer geometry = shape->GetGeometry();
mitk::Vector3D spacing = geometry->GetSpacing();
// calculate cornerpoints and extent from geometry with visualization offset
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
Point3D p0 = cornerPoints[0];
Point3D p1 = cornerPoints[1];
Point3D p2 = cornerPoints[2];
Point3D p4 = cornerPoints[4];
Point3D extent;
extent[0] =
sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2]));
extent[1] =
sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2]));
extent[2] =
sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2]));
// calculate center based on half way of the distance between two opposing cornerpoints
mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
if (m_Impl->HandlePropertyList.size() == 6)
{
// set handle positions
Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]);
Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]);
Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]);
Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]);
Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]);
Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]);
m_Impl->HandlePropertyList[0].SetPosition(pointLeft);
m_Impl->HandlePropertyList[1].SetPosition(pointRight);
m_Impl->HandlePropertyList[2].SetPosition(pointTop);
m_Impl->HandlePropertyList[3].SetPosition(pointBottom);
m_Impl->HandlePropertyList[4].SetPosition(pointFront);
m_Impl->HandlePropertyList[5].SetPosition(pointBack);
}
- // caculate face normals
+ // calculate face normals
double cubeFaceNormal0[3], cubeFaceNormal1[3], cubeFaceNormal2[3];
double a[3], b[3];
a[0] = (cornerPoints[5][0] - cornerPoints[6][0]);
a[1] = (cornerPoints[5][1] - cornerPoints[6][1]);
a[2] = (cornerPoints[5][2] - cornerPoints[6][2]);
b[0] = (cornerPoints[5][0] - cornerPoints[4][0]);
b[1] = (cornerPoints[5][1] - cornerPoints[4][1]);
b[2] = (cornerPoints[5][2] - cornerPoints[4][2]);
vtkMath::Cross(a, b, cubeFaceNormal0);
a[0] = (cornerPoints[0][0] - cornerPoints[6][0]);
a[1] = (cornerPoints[0][1] - cornerPoints[6][1]);
a[2] = (cornerPoints[0][2] - cornerPoints[6][2]);
b[0] = (cornerPoints[0][0] - cornerPoints[2][0]);
b[1] = (cornerPoints[0][1] - cornerPoints[2][1]);
b[2] = (cornerPoints[0][2] - cornerPoints[2][2]);
vtkMath::Cross(a, b, cubeFaceNormal1);
a[0] = (cornerPoints[2][0] - cornerPoints[7][0]);
a[1] = (cornerPoints[2][1] - cornerPoints[7][1]);
a[2] = (cornerPoints[2][2] - cornerPoints[7][2]);
b[0] = (cornerPoints[2][0] - cornerPoints[6][0]);
b[1] = (cornerPoints[2][1] - cornerPoints[6][1]);
b[2] = (cornerPoints[2][2] - cornerPoints[6][2]);
vtkMath::Cross(a, b, cubeFaceNormal2);
vtkMath::Normalize(cubeFaceNormal0);
vtkMath::Normalize(cubeFaceNormal1);
vtkMath::Normalize(cubeFaceNormal2);
// create cube for rendering bounding box
auto cube = vtkCubeSource::New();
cube->SetXLength(extent[0] / spacing[0]);
cube->SetYLength(extent[1] / spacing[1]);
cube->SetZLength(extent[2] / spacing[2]);
// calculates translation based on offset+extent not on the transformation matrix
vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix();
auto translation = vtkSmartPointer<vtkTransform>::New();
translation->Translate(center[0] - imageTransform->GetElement(0, 3),
center[1] - imageTransform->GetElement(1, 3),
center[2] - imageTransform->GetElement(2, 3));
auto transform = vtkSmartPointer<vtkTransform>::New();
transform->SetMatrix(imageTransform);
transform->PostMultiply();
transform->Concatenate(translation);
transform->Update();
cube->Update();
auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New();
transformFilter->SetInputData(cube->GetOutput());
transformFilter->SetTransform(transform);
transformFilter->Update();
cube->Delete();
vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput();
if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1))
{
localStorage->m_Actor->VisibilityOff();
localStorage->m_HandleActor->VisibilityOff();
localStorage->m_SelectedHandleActor->VisibilityOff();
return;
}
// estimate current image plane to decide whether the cube is visible or not
const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry();
if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry()))
return;
double origin[3];
origin[0] = planeGeometry->GetOrigin()[0];
origin[1] = planeGeometry->GetOrigin()[1];
origin[2] = planeGeometry->GetOrigin()[2];
double displayPlaneNormal[3];
displayPlaneNormal[0] = planeGeometry->GetNormal()[0];
displayPlaneNormal[1] = planeGeometry->GetNormal()[1];
displayPlaneNormal[2] = planeGeometry->GetNormal()[2];
vtkMath::Normalize(displayPlaneNormal);
localStorage->m_CuttingPlane->SetOrigin(origin);
localStorage->m_CuttingPlane->SetNormal(displayPlaneNormal);
// add cube polydata to local storage
localStorage->m_Cutter->SetInputData(polydata);
localStorage->m_Cutter->SetGenerateCutScalars(1);
localStorage->m_Cutter->Update();
if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor))
localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor);
if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor))
localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor);
vtkCoordinate *tcoord = vtkCoordinate::New();
tcoord->SetCoordinateSystemToWorld();
localStorage->m_HandleMapper->SetTransformCoordinate(tcoord);
tcoord->Delete();
if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow
{
mitk::DoubleProperty::Pointer handleSizeProperty =
dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor"));
ScalarType initialHandleSize;
if (handleSizeProperty != nullptr)
initialHandleSize = handleSizeProperty->GetValue();
else
initialHandleSize = 0.02;
mitk::Point2D displaySize = renderer->GetDisplaySizeInMM();
double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize;
auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New();
unsigned int handleIdx = 0;
// add handles and their assigned properties to the local storage
mitk::IntProperty::Pointer activeHandleId =
dynamic_cast<mitk::IntProperty *>(node->GetProperty("Bounding Shape.Active Handle ID"));
double angle0 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal0)));
if (angle0 > 179.0) angle0 -= 180.0;
double angle1 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal1)));
if (angle1 > 179.0) angle1 -= 180.0;
double angle2 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal2)));
if (angle2 > 179.0) angle2 -= 180.0;
bool visible = false;
bool selected = false;
for (auto& handle : localStorage->m_Handles)
{
Point3D handleCenter = m_Impl->HandlePropertyList[handleIdx].GetPosition();
handle->SetXLength(handleSize);
handle->SetYLength(handleSize);
handle->SetZLength(handleSize);
handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]);
// show handles only if the corresponding face is aligned to the render window
if ( (handleIdx != 0 && handleIdx != 1 && std::abs(angle0) < 0.1) || // handles 0 and 1
(handleIdx != 2 && handleIdx != 3 && std::abs(angle1) < 0.1) || // handles 2 and 3
(handleIdx != 4 && handleIdx != 5 && std::abs(angle2) < 0.1) ) // handles 4 and 5
{
if (activeHandleId == nullptr)
{
appendPoly->AddInputConnection(handle->GetOutputPort());
}
else
{
if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[handleIdx].GetIndex()))
{
appendPoly->AddInputConnection(handle->GetOutputPort());
}
else
{
handle->Update();
localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput());
localStorage->m_SelectedHandleActor->VisibilityOn();
selected = true;
}
}
visible = true;
}
++handleIdx;
}
if (visible)
{
appendPoly->Update();
}
else
{
localStorage->m_HandleActor->VisibilityOff();
localStorage->m_SelectedHandleActor->VisibilityOff();
}
auto stripper = vtkSmartPointer<vtkStripper>::New();
stripper->SetInputData(localStorage->m_Cutter->GetOutput());
stripper->Update();
auto cutPolyData = vtkSmartPointer<vtkPolyData>::New();
cutPolyData->SetPoints(stripper->GetOutput()->GetPoints());
cutPolyData->SetPolys(stripper->GetOutput()->GetLines());
localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData);
this->ApplyColorAndOpacityProperties(renderer, localStorage->m_Actor);
if (activeHandleId != nullptr)
{
localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0);
}
else
{
localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1);
}
localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput());
// add parts to the overall storage
localStorage->m_PropAssembly->AddPart(localStorage->m_Actor);
localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor);
if (selected)
{
localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor);
}
localStorage->m_PropAssembly->VisibilityOn();
localStorage->m_Actor->VisibilityOn();
localStorage->m_HandleActor->VisibilityOn();
}
else
{
localStorage->m_PropAssembly->VisibilityOff();
localStorage->m_Actor->VisibilityOff();
localStorage->m_HandleActor->VisibilityOff();
localStorage->m_SelectedHandleActor->VisibilityOff();
localStorage->UpdateGenerateDataTime();
}
localStorage->UpdateGenerateDataTime();
}
}
vtkProp *mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer *renderer)
{
return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly;
}
void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor)
{
auto* property = actor->GetProperty();
std::array<float, 3> color = { 1.0, 0.0, 0.0 };
this->GetDataNode()->GetColor(color.data(), renderer);
property->SetColor(color[0], color[1], color[2]);
float opacity = 0.2f;
this->GetDataNode()->GetOpacity(opacity, renderer);
property->SetOpacity(opacity);
}
diff --git a/Modules/CEST/include/mitkCESTImageNormalizationFilter.h b/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
index 8a0dde411f..5ff20f9440 100644
--- a/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
+++ b/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
@@ -1,85 +1,85 @@
/*============================================================================
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 mitkCESTImageNormalizationFilter_h
#define mitkCESTImageNormalizationFilter_h
#include <MitkCESTExports.h>
// MITK
#include "mitkImageToImageFilter.h"
namespace mitk
{
/** \brief Normalization filter for CEST images.
*
* This filter can be used to normalize CEST images, it only works with 4D images and assumes that the input
- * mitk::Image has a property called mitk::CustomTagParser::m_OffsetsPropertyName, whith offsets separated by
+ * mitk::Image has a property called mitk::CustomTagParser::m_OffsetsPropertyName, with offsets separated by
* spaces. The number of offsets has to match the number of timesteps.
*
* Each timestep with a corresponding offset greater than 299 or less than -299 will be interpreted as normalization (M0) image.
* If only one M0 image is present normalization will be done by dividing the voxel value by the corresponding
* M0 voxel value. If multiple M0 images are present normalization between any two M0 images will be done by
* dividing by a linear interpolation between the two.
* The M0 images themselves will be removed from the result.
* The output image will have the same 3D geometry as the input image, a time geometry only consisting of non M0 images and a double pixel type.
*/
class MITKCEST_EXPORT CESTImageNormalizationFilter : public ImageToImageFilter
{
public:
mitkClassMacro(CESTImageNormalizationFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
protected:
/*!
\brief standard constructor
*/
CESTImageNormalizationFilter();
/*!
\brief standard destructor
*/
~CESTImageNormalizationFilter() override;
/*!
\brief Method generating the output information of this filter (e.g. image dimension, image type, etc.).
The interface ImageToImageFilter requires this implementation. Everything is taken from the input image.
*/
void GenerateOutputInformation() override;
/*!
\brief Method generating the output of this filter. Called in the updated process of the pipeline.
This method generates the normalized output image.
*/
void GenerateData() override;
/** Internal templated method that normalizes across timesteps
*/
template <typename TPixel, unsigned int VImageDimension>
void NormalizeTimeSteps(const itk::Image<TPixel, VImageDimension>* image);
/// Offsets without M0s
std::string m_RealOffsets;
/// non M0 indices
std::vector< unsigned int > m_NonM0Indices;
};
/** This helper function can be used to check if an image was already normalized.
* The function assumes that an image that is not normalized is indicated by the following properties:
* - mitk::Image has a property called mitk::CEST_PROPERTY_NAME_OFFSETS, with offsets separated by spaces.
* - The number of offsets has to match the number of timesteps.
* - At least one of the offsets is a normalization (M0) image. M0 are indicated by offsets greater than 299 or less than -299.
*/
MITKCEST_EXPORT bool IsNotNormalizedCESTImage(const Image* cestImage);
} // END mitk namespace
#endif
diff --git a/Modules/CEST/include/mitkCESTPropertyHelper.h b/Modules/CEST/include/mitkCESTPropertyHelper.h
index 73d34c2e79..e65ff055ec 100644
--- a/Modules/CEST/include/mitkCESTPropertyHelper.h
+++ b/Modules/CEST/include/mitkCESTPropertyHelper.h
@@ -1,60 +1,60 @@
/*============================================================================
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 mitkCESTPropertyHelper_h
#define mitkCESTPropertyHelper_h
#include "mitkIPropertyProvider.h"
#include "mitkIPropertyOwner.h"
#include "MitkCESTExports.h"
namespace mitk
{
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PREPERATIONTYPE();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_RECOVERYMODE();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_SPOILINGTYPE();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_OFFSETS();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TREC();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_FREQ();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PULSEDURATION();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_B1Amplitude();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_DutyCycle();
/**Helper function that gets the CEST B1 amplitude property ("CEST.B1Amplitude") from the passed property provider.
If it is not possible to generate/get the value an mitk::Exception will be thrown.*/
double MITKCEST_EXPORT GetCESTB1Amplitude(const IPropertyProvider* provider);
/**Helper function that gets the CEST frequency property ("CEST.FREQ") from the input image.
If it is not possible to generate/get the value an mitk::Exception will be thrown.
The value is returned in [MHz]. Normally in the property it is stored in [Hz].*/
double MITKCEST_EXPORT GetCESTFrequency(const IPropertyProvider* provider);
/**Helper function that sets the CEST frequency property ("CEST.FREQ") in the passed owner.
If it owner is nullptr nothing will be done.
The value is passed in [MHz] and set in the property in [Hz].*/
void MITKCEST_EXPORT SetCESTFrequencyMHz(IPropertyOwner* owner, double freqInMHz);
/**Helper function that gets the CEST pulse duration property ("CEST.PulseDuration") from the input image.
If it is not possible to generate/get the value an mitk::Exception will be thrown.
The value is returned in [s]. Normally in the property it is stored in micro secs.*/
double MITKCEST_EXPORT GetCESTPulseDuration(const IPropertyProvider* provider);
/**Helper function that gets the CEST duty cycle property ("CEST.DutyCycle") from the input image.
If it is not possible to generate/get the value an mitk::Exception will be thrown.
- The value is returned as scaling factor (1 == 100%), in contrast to the porperty where it is stored as
+ The value is returned as scaling factor (1 == 100%), in contrast to the property where it is stored as
a percentage value (e.g. 56 %, so the function return will be 0.56).*/
double MITKCEST_EXPORT GetCESTDutyCycle(const IPropertyProvider* provider);
}
#endif
diff --git a/Modules/CEST/include/mitkCustomTagParser.h b/Modules/CEST/include/mitkCustomTagParser.h
index 2ba6b107b5..8b7511031e 100644
--- a/Modules/CEST/include/mitkCustomTagParser.h
+++ b/Modules/CEST/include/mitkCustomTagParser.h
@@ -1,139 +1,139 @@
/*============================================================================
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 mitkCustomTagParser_h
#define mitkCustomTagParser_h
#include<mitkPropertyList.h>
#include<mitkTemporoSpatialStringProperty.h>
#include <MitkCESTExports.h>
namespace mitk
{
/**
The custom tag parser can be used to parse the custom dicom tag of the siemens private tag
(0x0029, 0x1020) to extract relevant CEST data.
An initial parsing determines whether the provided string belongs to CEST data at all.
- To make the check and extract the revision number the following rules are aplied: \n
+ To make the check and extract the revision number the following rules are applied: \n
<ol>
<li>Sequence name (tSequenceFileName) must either
<ol>
<li>start with the substring "CEST" (case insensitiv), or</li>
<li>contain the substring "_CEST" (case insensitiv).</li>
</ol>
</li>
<li>Sequence name (tSequenceFileName) must contain the substring "_Rev" (case insensitiv).</li>
<li>All numbers after "_Rev" represent the revision number; until either
<ol>
<li>the next _, or</li>
<li>end of sequence name.</li>
</ol>
</li>
</ol>
Which custom parameters to save and to which property name can be controlled by a json file.
This file can be either provided as a resource for the MitkCEST module during compilation or
placed next to the MitkCEST library in your binary folder.
The expected format for the file "REVISIONNUMBER.json": <br>
{ <br>
"REVISIONNUMBER" : "revision_json", <br>
"sWiPMemBlock.alFree[1]" : "AdvancedMode", <br>
"sWiPMemBlock.alFree[2]" : "RetreatMode" <br>
} <br>
where :
<ul>
<li> REVISIONNUMBER is the revision number of this json parameter mapping (files with non digit characters in their
name will be ignored)
<li> sWiPMemBlock.alFree[1] is the name of one parameter in the private dicom tag
<li> AdvancedMode is the name of the property the content of sWiPMemBlock.alFree[1] should be saved to
</ul>
\note It is assumed that the entire content of tag (0x0029, 0x1020) is provided and that it es hex encoded
(12\23\04...).
If the sampling type is list it will try to access LIST.txt at the location provided in the constructor to
read the offsets.
*/
class MITKCEST_EXPORT CustomTagParser
{
public:
/// the constructor expects a path to one of the files to be loaded or the directory of the dicom files
CustomTagParser(std::string relevantFile);
/// parse the provided dicom property and return a property list based on the closest revision parameter mapping
mitk::PropertyList::Pointer ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty);
/// parse the provided string and return a property list based on the closest revision parameter mapping
mitk::PropertyList::Pointer ParseDicomPropertyString(std::string dicomPropertyString);
static std::string ReadListFromFile(const std::string& filePath);
/** Extract the revision out of the passed sequenceFileName. If the file name is not a valid CEST file name
(see rules in the class documentation) exceptions will be thrown. If the file name is valid but contains no
revision number an empty string will be returned.
*/
static std::string ExtractRevision(std::string sequenceFileName);
void SetParseStrategy(std::string parseStrategy);
void SetRevisionMappingStrategy(std::string revisionMappingStrategy);
/// name of the property for the data acquisition revision
static const std::string m_RevisionPropertyName;
/// name of the property for the json parameter mapping revision
static const std::string m_JSONRevisionPropertyName;
/// prefix for all CEST related property names
static const std::string m_CESTPropertyPrefix;
protected:
std::string GetRevisionAppropriateJSONString(std::string revisionString);
void GetClosestLowerRevision(std::string revisionString);
std::string GetClosestLowerRevision(std::string revisionString, std::vector<int> availableRevisionsVector);
/// Decides whether or not the image is likely to be a T1Map, if not it is assumed to be a CEST sequence
bool IsT1Sequence(std::string preparationType, std::string recoveryMode, std::string spoilingType, std::string revisionString);
- /// Get a string filled with the properly formated offsets based on the sampling type and offset
+ /// Get a string filled with the properly formatted offsets based on the sampling type and offset
std::string GetOffsetString(std::string samplingType, std::string offset, std::string measurements);
/// returns a vector revision numbers of all REVISIONNUMBER.json found beside the MitkCEST library
std::vector<int> GetExternalRevisions();
/// returns a vector revision numbers of all REVISIONNUMBER.json provided as resources during the compile
std::vector<int> GetInternalRevisions();
/// returns the path where external jsons are expected to be located
std::string GetExternalJSONDirectory();
/// the closest lower revision provided as resource, empty if none found
std::string m_ClosestInternalRevision;
/// the closest lower revision provided as a json beside the library, empty if none found
std::string m_ClosestExternalRevision;
/// revision independent mapping to inject into the revision dependent json string
static const std::string m_RevisionIndependentMapping;
/// default revision dependent json string if none is found
static const std::string m_DefaultJsonString;
/// path to the dicom data
std::string m_DicomDataPath;
/// Should the kind of data be automatically determined or should it be parsed as a specific one
std::string m_ParseStrategy;
/// How to handle parameter mapping based on absent revision jsons
std::string m_RevisionMappingStrategy;
};
}
#endif
diff --git a/Modules/CEST/include/mitkExtractCESTOffset.h b/Modules/CEST/include/mitkExtractCESTOffset.h
index 031d55fe1a..75dcde577c 100644
--- a/Modules/CEST/include/mitkExtractCESTOffset.h
+++ b/Modules/CEST/include/mitkExtractCESTOffset.h
@@ -1,39 +1,39 @@
/*============================================================================
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 mitkExtractCESTOffset_h
#define mitkExtractCESTOffset_h
#include <mitkBaseData.h>
#include "MitkCESTExports.h"
namespace mitk
{
/**Helper function that gets the CEST offset property ("CEST.Offsets") from the input
image as vector of ScalarType.
- If it is not possible to generate/get the offset an mitk::Excpetion will be thrown.
+ If it is not possible to generate/get the offset an mitk::Exception will be thrown.
The values of the vector are in [ppm].
@post Number of extracted offsets equal the number of timesteps of the image.
*/
MITKCEST_EXPORT std::vector<ScalarType> ExtractCESTOffset(const BaseData* image);
/**Helper function that gets the CEST offset property ("CEST.TREC") from the input image as vector of ScalarType.
- If it is not possible to generate/get the T1 times an mitk::Excpetion will be thrown.
+ If it is not possible to generate/get the T1 times an mitk::Exception will be thrown.
The values of the vector are in [sec]. In the property they are stored in [ms] and scaled appropriately
before returning.
@post Number of extracted T1 times equal the number of timesteps of the image.
*/
MITKCEST_EXPORT std::vector<ScalarType> ExtractCESTT1Time(const BaseData* image);
}
#endif
diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp
index ae68040759..ae5333dc96 100644
--- a/Modules/CEST/src/mitkCustomTagParser.cpp
+++ b/Modules/CEST/src/mitkCustomTagParser.cpp
@@ -1,855 +1,855 @@
/*============================================================================
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 "mitkCustomTagParser.h"
#include <mitkProperties.h>
#include <mitkStringProperty.h>
#include "mitkCESTPropertyHelper.h"
#include "mitkIPropertyPersistence.h"
#include "usGetModuleContext.h"
#include "usModule.h"
#include "usModuleContext.h"
#include "usModuleResource.h"
#include "usModuleResourceStream.h"
#include <itksys/SystemTools.hxx>
#include <Poco/Glob.h>
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <algorithm>
#include <cstdint>
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
using namespace nlohmann;
namespace
{
mitk::IPropertyPersistence *GetPersistenceService()
{
mitk::IPropertyPersistence *result = nullptr;
std::vector<us::ServiceReference<mitk::IPropertyPersistence>> persRegisters =
us::GetModuleContext()->GetServiceReferences<mitk::IPropertyPersistence>();
if (!persRegisters.empty())
{
if (persRegisters.size() > 1)
{
MITK_WARN << "Multiple property description services found. Using just one.";
}
result = us::GetModuleContext()->GetService<mitk::IPropertyPersistence>(persRegisters.front());
}
return result;
};
}
const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST.";
const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision";
const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json";
const std::string mitk::CustomTagParser::m_RevisionIndependentMapping = R"(
"sProtConsistencyInfo.tSystemType" : "SysType",
"sProtConsistencyInfo.flNominalB0" : "NominalB0",
"sTXSPEC.asNucleusInfo[0].lFrequency" : "FREQ",
"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude" : "RefAmp",
"alTR[0]" : "TR",
"alTE[0]" : "TE",
"lAverages" : "averages",
"lRepetitions" : "repetitions",
"adFlipAngleDegree[0]" : "ImageFlipAngle",
"lTotalScanTimeSec" : "TotalScanTime",
)";
const std::string mitk::CustomTagParser::m_DefaultJsonString = R"({
"default mapping, corresponds to revision 1416" : "revision_json",
"sWiPMemBlock.alFree[1]" : "AdvancedMode",
"sWiPMemBlock.alFree[2]" : "RecoveryMode",
"sWiPMemBlock.alFree[3]" : "DoubleIrrMode",
"sWiPMemBlock.alFree[4]" : "BinomMode",
"sWiPMemBlock.alFree[5]" : "MtMode",
"sWiPMemBlock.alFree[6]" : "PreparationType",
"sWiPMemBlock.alFree[7]" : "PulseType",
"sWiPMemBlock.alFree[8]" : "SamplingType",
"sWiPMemBlock.alFree[9]" : "SpoilingType",
"sWiPMemBlock.alFree[10]" : "measurements",
"sWiPMemBlock.alFree[11]" : "NumberOfPulses",
"sWiPMemBlock.alFree[12]" : "NumberOfLockingPulses",
"sWiPMemBlock.alFree[13]" : "PulseDuration",
"sWiPMemBlock.alFree[14]" : "DutyCycle",
"sWiPMemBlock.alFree[15]" : "RecoveryTime",
"sWiPMemBlock.alFree[16]" : "RecoveryTimeM0",
"sWiPMemBlock.alFree[17]" : "ReadoutDelay",
"sWiPMemBlock.alFree[18]" : "BinomDuration",
"sWiPMemBlock.alFree[19]" : "BinomDistance",
"sWiPMemBlock.alFree[20]" : "BinomNumberofPulses",
"sWiPMemBlock.alFree[21]" : "BinomPreRepetions",
"sWiPMemBlock.alFree[22]" : "BinomType",
"sWiPMemBlock.adFree[1]" : "Offset",
"sWiPMemBlock.adFree[2]" : "B1Amplitude",
"sWiPMemBlock.adFree[3]" : "AdiabaticPulseMu",
"sWiPMemBlock.adFree[4]" : "AdiabaticPulseBW",
"sWiPMemBlock.adFree[5]" : "AdiabaticPulseLength",
"sWiPMemBlock.adFree[6]" : "AdiabaticPulseAmp",
"sWiPMemBlock.adFree[7]" : "FermiSlope",
"sWiPMemBlock.adFree[8]" : "FermiFWHM",
"sWiPMemBlock.adFree[9]" : "DoubleIrrDuration",
"sWiPMemBlock.adFree[10]" : "DoubleIrrAmplitude",
"sWiPMemBlock.adFree[11]" : "DoubleIrrRepetitions",
"sWiPMemBlock.adFree[12]" : "DoubleIrrPreRepetitions"
})";
mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("")
{
std::string pathToDirectory;
std::string fileName;
itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName);
m_DicomDataPath = pathToDirectory;
m_ParseStrategy = "Automatic";
m_RevisionMappingStrategy = "Fuzzy";
}
std::string mitk::CustomTagParser::ExtractRevision(std::string sequenceFileName)
{
- //all rules are case insesitive. Thus we convert everything to lower case
+ //all rules are case insensitive. Thus we convert everything to lower case
//in order to check everything only once.
std::string cestPrefix = "cest";
std::string cestPrefix2 = "_cest";
std::string cestPrefix3 = "\\cest"; //this version covers the fact that the strings extracted
- //from the SIEMENS tag has an additional prefix that is seperated by backslash.
+ //from the SIEMENS tag has an additional prefix that is separated by backslash.
std::string revisionPrefix = "_rev";
std::transform(sequenceFileName.begin(), sequenceFileName.end(), sequenceFileName.begin(), ::tolower);
bool isCEST = sequenceFileName.compare(0, cestPrefix.length(), cestPrefix) == 0;
std::size_t foundPosition = 0;
if (!isCEST)
{
foundPosition = sequenceFileName.find(cestPrefix2);
isCEST = foundPosition != std::string::npos;
}
if (!isCEST)
{
foundPosition = sequenceFileName.find(cestPrefix3);
isCEST = foundPosition != std::string::npos;
}
if (!isCEST)
{
mitkThrow() << "Invalid CEST sequence file name. No CEST prefix found. Could not extract revision.";
}
foundPosition = sequenceFileName.find(revisionPrefix, foundPosition);
if (foundPosition == std::string::npos)
{
mitkThrow() << "Invalid CEST sequence file name. No revision prefix was found in CEST sequence file name. Could not extract revision.";
}
std::string revisionString = sequenceFileName.substr(foundPosition + revisionPrefix.length(), std::string::npos);
std::size_t firstNoneNumber = revisionString.find_first_not_of("0123456789");
if (firstNoneNumber != std::string::npos)
{
revisionString.erase(firstNoneNumber, std::string::npos);
}
return revisionString;
}
bool mitk::CustomTagParser::IsT1Sequence(std::string preparationType,
std::string recoveryMode,
std::string spoilingType,
std::string revisionString)
{
bool isT1 = false;
// if a forced parse strategy is set, use that one
if ("T1" == m_ParseStrategy)
{
return true;
}
if ("CEST/WASABI" == m_ParseStrategy)
{
return false;
}
if (("T1Recovery" == preparationType) || ("T1Inversion" == preparationType))
{
isT1 = true;
}
// How to interpret the recoveryMode depends on the age of the sequence
// older sequences use 0 = false and 1 = true, newer ones 1 = false and 2 = true.
// A rough rule of thumb is to assume that if the SpoilingType is 0, then the first
// convention is chosen, if it is 1, then the second applies. Otherwise
// we assume revision 1485 and newer to follow the new convention.
// This unfortunate heuristic is due to somewhat arbitrary CEST sequence implementations.
if (!isT1)
{
std::string thisIsTrue = "1";
std::string thisIsFalse = "0";
if ("0" == spoilingType)
{
thisIsFalse = "0";
thisIsTrue = "1";
}
else if ("1" == spoilingType)
{
thisIsFalse = "1";
thisIsTrue = "2";
}
else
{
int revisionNrWeAssumeToBeDifferenciating = 1485;
if (std::stoi(revisionString) - revisionNrWeAssumeToBeDifferenciating < 0)
{
thisIsFalse = "0";
thisIsTrue = "1";
}
else
{
thisIsFalse = "1";
thisIsTrue = "2";
}
}
if (thisIsFalse == recoveryMode)
{
isT1 = false;
}
else if (thisIsTrue == recoveryMode)
{
isT1 = true;
}
}
return isT1;
}
mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomPropertyString(std::string dicomPropertyString)
{
auto results = mitk::PropertyList::New();
if ("" == dicomPropertyString)
{
//MITK_ERROR << "Could not parse empty custom dicom string";
return results;
}
auto comp = [](const std::string& s1, const std::string& s2)
{
return boost::algorithm::lexicographical_compare(s1, s2, boost::algorithm::is_iless());
};
std::map<std::string, std::string, decltype(comp)> privateParameters(comp);
// The Siemens private tag contains information like "43\52\23\34".
// We jump over each "\" and convert the number;
std::string bytes;
{
const std::size_t SUBSTR_LENGTH = 2;
const std::size_t INPUT_LENGTH = dicomPropertyString.length();
if (INPUT_LENGTH < SUBSTR_LENGTH)
return results;
const std::size_t MAX_INPUT_OFFSET = INPUT_LENGTH - SUBSTR_LENGTH;
bytes.reserve(INPUT_LENGTH / 3 + 1);
try
{
for (std::size_t i = 0; i <= MAX_INPUT_OFFSET; i += 3)
{
std::string byte_string = dicomPropertyString.substr(i, SUBSTR_LENGTH);
int byte = static_cast<std::string::value_type>(std::stoi(byte_string.c_str(), nullptr, 16));
bytes.push_back(byte);
}
}
catch (const std::invalid_argument&) // std::stoi() could not perform conversion
{
return results;
}
}
// extract parameter list
std::string parameterListString;
{
const std::string ASCCONV_MARKER = "###";
const std::string ASCCONV_BEGIN = "### ASCCONV BEGIN";
const std::string ASCCONV_END = "### ASCCONV END";
auto ascconvBeginPos = bytes.find(ASCCONV_BEGIN);
if (std::string::npos == ascconvBeginPos)
return results;
ascconvBeginPos += ASCCONV_BEGIN.length();
ascconvBeginPos = bytes.find(ASCCONV_MARKER, ascconvBeginPos);
if (std::string::npos == ascconvBeginPos)
return results;
ascconvBeginPos += ASCCONV_MARKER.length(); // closing "###"
auto ascconvEndPos = bytes.find(ASCCONV_END, ascconvBeginPos);
if (std::string::npos == ascconvEndPos)
return results;
auto count = ascconvEndPos - ascconvBeginPos;
parameterListString = bytes.substr(ascconvBeginPos, count);
}
boost::replace_all(parameterListString, "\r\n", "\n");
boost::replace_all(parameterListString, "\t", "");
boost::char_separator<char> newlineSeparator("\n");
boost::tokenizer<boost::char_separator<char>> parameters(parameterListString, newlineSeparator);
for (const auto &parameter : parameters)
{
std::vector<std::string> parts;
boost::split(parts, parameter, boost::is_any_of("="));
if (parts.size() == 2)
{
parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end());
parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space
privateParameters[parts[0]] = parts[1];
}
}
std::string revisionString = "";
try
{
revisionString = ExtractRevision(privateParameters["tSequenceFileName"]);
}
catch (const std::exception &e)
{
MITK_ERROR << "Cannot deduce revision information. Reason: "<< e.what();
return results;
}
results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString));
std::string jsonString = GetRevisionAppropriateJSONString(revisionString);
json root;
try
{
root = json::parse(jsonString);
}
catch (const json::exception &e)
{
mitkThrow() << "Could not parse json file. Error was:\n" << e.what();
}
for (const auto &it : root.items())
{
if (it.value().is_string())
{
auto propertyName = m_CESTPropertyPrefix + it.value().get<std::string>();
if (m_JSONRevisionPropertyName == propertyName)
{
results->SetProperty(propertyName, mitk::StringProperty::New(it.key()));
}
else
{
results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.key()]));
}
}
else
{
MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file.";
}
}
std::string offset = "";
std::string measurements = "";
results->GetStringProperty("CEST.Offset", offset);
results->GetStringProperty("CEST.measurements", measurements);
if (measurements.empty())
{
std::string stringRepetitions = "";
results->GetStringProperty("CEST.repetitions", stringRepetitions);
std::string stringAverages = "";
results->GetStringProperty("CEST.averages", stringAverages);
const auto ERROR_STRING = "Could not find measurements, fallback assumption of repetitions + averages could not be determined either.";
if (!stringRepetitions.empty() && !stringAverages.empty())
{
std::stringstream measurementStream;
try
{
measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages);
measurements = measurementStream.str();
MITK_INFO << "Could not find measurements, assuming repetitions + averages. That is: " << measurements;
}
catch (const std::invalid_argument&)
{
MITK_ERROR << ERROR_STRING;
}
}
else
{
MITK_WARN << ERROR_STRING;
}
}
std::string preparationType = "";
std::string recoveryMode = "";
std::string spoilingType = "";
results->GetStringProperty(CEST_PROPERTY_NAME_PREPERATIONTYPE().c_str(), preparationType);
results->GetStringProperty(CEST_PROPERTY_NAME_RECOVERYMODE().c_str(), recoveryMode);
results->GetStringProperty(CEST_PROPERTY_NAME_SPOILINGTYPE().c_str(), spoilingType);
if (this->IsT1Sequence(preparationType, recoveryMode, spoilingType, revisionString))
{
MITK_INFO << "Parsed as T1 image";
std::stringstream trecStream;
std::string trecPath = m_DicomDataPath + "/TREC.txt";
auto trec = ReadListFromFile(trecPath);
if(trec.empty())
{
MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath;
}
results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trec.c_str());
}
else
{
MITK_INFO << "Parsed as CEST or WASABI image";
std::string sampling = "";
bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling);
if (hasSamplingInformation)
{
std::string offsets = GetOffsetString(sampling, offset, measurements);
results->SetStringProperty(CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets.c_str());
}
else
{
MITK_WARN << "Could not determine sampling type.";
}
}
//persist all properties
mitk::IPropertyPersistence *persSrv = GetPersistenceService();
if (persSrv)
{
auto propertyMap = results->GetMap();
for (auto const &prop : *propertyMap)
{
PropertyPersistenceInfo::Pointer info = PropertyPersistenceInfo::New();
std::string key = prop.first;
std::replace(key.begin(), key.end(), '.', '_');
info->SetNameAndKey(prop.first, key);
persSrv->AddInfo(info);
}
}
return results;
}
std::string mitk::CustomTagParser::ReadListFromFile(const std::string& filePath)
{
std::stringstream listStream;
std::ifstream list(filePath.c_str());
list.imbue(std::locale("C"));
if (list.good())
{
std::string currentValue;
while (std::getline(list, currentValue))
{
listStream << currentValue << " ";
}
}
return listStream.str();
}
mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty)
{
if (!dicomProperty)
{
MITK_ERROR << "DICOM property empty";
}
auto results = mitk::PropertyList::New();
if (dicomProperty)
{
results = ParseDicomPropertyString(dicomProperty->GetValue());
}
return results;
}
std::vector<int> mitk::CustomTagParser::GetInternalRevisions()
{
const std::vector<us::ModuleResource> configs =
us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false);
std::vector<int> availableRevisionsVector;
for (const auto& resource : configs)
{
availableRevisionsVector.push_back(std::stoi(resource.GetBaseName()));
}
return availableRevisionsVector;
}
std::vector<int> mitk::CustomTagParser::GetExternalRevisions()
{
std::string stringToJSONDirectory = GetExternalJSONDirectory();
std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json";
std::set<std::string> JsonFiles;
Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS);
std::vector<int> availableRevisionsVector;
for (const auto& jsonpath : JsonFiles)
{
std::string jsonDir;
std::string jsonName;
itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName);
std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName);
// disregard jsons which contain letters in their name
bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos);
if(onlyNumbers)
{
availableRevisionsVector.push_back(std::stoi(revision));
}
}
return availableRevisionsVector;
}
std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector<int> availableRevisionsVector)
{
// descending order
std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>());
int revision = std::stoi(revisionString);
int index = 0;
int numberOfRevisions = availableRevisionsVector.size();
while (index < numberOfRevisions)
{
// current mapping still has a higher revision number
if ((availableRevisionsVector[index] - revision) > 0)
{
++index;
}
else
{
break;
}
}
if (index < numberOfRevisions)
{
std::stringstream foundRevisionStream;
foundRevisionStream << availableRevisionsVector[index];
return foundRevisionStream.str();
}
return "";
}
void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString)
{
m_ClosestInternalRevision = GetClosestLowerRevision(revisionString, GetInternalRevisions());
m_ClosestExternalRevision = GetClosestLowerRevision(revisionString, GetExternalRevisions());
if ("Strict" == m_RevisionMappingStrategy && !((0 == m_ClosestInternalRevision.compare(revisionString)) ||
(0 == m_ClosestExternalRevision.compare(revisionString))))
{ // strict revision mapping and neither revision does match the dicom meta data
std::stringstream errorMessageStream;
errorMessageStream << "\nCould not parse dicom data in strict mode, data revision " << revisionString
<< " has no known matching parameter mapping. To use the closest known older parameter mapping select the "
<< "\"Fuzzy\" revision mapping option when loading the data.\n"
<< "\nCurrently known revision mappings are:\n Precompiled:";
for (const auto revision : GetInternalRevisions())
{
errorMessageStream << " " << revision;
}
errorMessageStream << "\n External:";
for (const auto revision : GetExternalRevisions())
{
errorMessageStream << " " << revision;
}
errorMessageStream << "\n\nExternal revision mapping descriptions should be located at\n\n";
std::string stringToJSONDirectory = GetExternalJSONDirectory();
errorMessageStream << stringToJSONDirectory;
errorMessageStream << "\n\nTo provide an external mapping for this revision create a " << revisionString
<< ".json there. You might need to create the directory first.";
mitkThrow() << errorMessageStream.str();
}
}
std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString)
{
std::string returnValue = "";
if ("" == revisionString)
{
MITK_WARN << "Could not extract revision";
}
else
{
GetClosestLowerRevision(revisionString);
bool useExternal = false;
bool useInternal = false;
if ("" != m_ClosestExternalRevision)
{
useExternal = true;
}
if ("" != m_ClosestInternalRevision)
{
useInternal = true;
}
if (useExternal && useInternal)
{
if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision))
{
useExternal = false;
}
}
if (useExternal)
{
std::string stringToJSONDirectory = GetExternalJSONDirectory();
std::string prospectiveJsonPath = stringToJSONDirectory + "/" + m_ClosestExternalRevision + ".json";
std::ifstream externalJSON(prospectiveJsonPath.c_str());
if (externalJSON.good())
{
MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath;
std::stringstream buffer;
buffer << externalJSON.rdbuf();
returnValue = buffer.str();
useInternal = false;
}
}
if (useInternal)
{
std::string filename = m_ClosestInternalRevision + ".json";
us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename);
if (jsonResource.IsValid() && jsonResource.IsFile())
{
MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision "
<< m_ClosestInternalRevision;
us::ModuleResourceStream jsonStream(jsonResource);
std::stringstream buffer;
buffer << jsonStream.rdbuf();
returnValue = buffer.str();
}
}
}
if ("" == returnValue)
{
MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString
<< ", using default mapping.";
returnValue = m_DefaultJsonString;
}
// inject the revision independent mapping before the first newline
{
returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping);
}
return returnValue;
}
std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements)
{
std::stringstream results;
results.imbue(std::locale("C"));
std::string normalizationIndicatingOffset = "-300";
double offsetDouble = 0.0;
int measurementsInt = 0;
bool validOffset = false;
bool validMeasurements = false;
if ("" != offset)
{
validOffset = true;
offsetDouble = std::stod(offset);
}
if ("" != measurements)
{
validMeasurements = true;
measurementsInt = std::stoi(measurements);
}
std::vector<double> offsetVector;
if (validOffset && validMeasurements)
{
for (int step = 0; step < measurementsInt -1; ++step)
{
double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0);
offsetVector.push_back(currentOffset);
}
}
else
{
MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type.";
}
if (samplingType == "1" || samplingType == "Regular")
{
if (validOffset && validMeasurements)
{
results << normalizationIndicatingOffset << " ";
for (const auto& entry : offsetVector)
{
results << entry << " ";
}
}
}
else if (samplingType == "2" || samplingType == "Alternating")
{
if (validOffset && validMeasurements)
{
results << normalizationIndicatingOffset << " ";
for (auto& entry : offsetVector)
{
entry = std::abs(entry);
}
std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>());
for (unsigned int index = 0; index < offsetVector.size(); ++index)
{
offsetVector[index] = std::pow(-1, index) * offsetVector[index];
}
for (auto& entry : offsetVector)
{
results << entry << " ";
}
}
}
else if (samplingType == "3" || samplingType == "List")
{
std::string listPath = m_DicomDataPath + "/LIST.txt";
auto values = ReadListFromFile(listPath);
if (!values.empty())
{
results << values;
}
else
{
MITK_ERROR << "Could not load list at " << listPath;
}
}
else if (samplingType == "4" || samplingType == "SingleOffset")
{
if (validOffset && validMeasurements)
{
results << normalizationIndicatingOffset << " ";
for (int step = 0; step < measurementsInt - 1; ++step)
{
results << offsetDouble << " ";
}
}
}
else
{
MITK_WARN << "Encountered unknown sampling type.";
}
std::string resultString = results.str();
// replace multiple spaces by a single space
std::string::iterator newEnditerator =
std::unique(resultString.begin(), resultString.end(),
[=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); }
);
resultString.erase(newEnditerator, resultString.end());
if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' '))
{
resultString.erase(resultString.end() - 1, resultString.end());
}
if ((resultString.length() > 0) && (resultString.at(0) == ' '))
{
resultString.erase(resultString.begin(), ++(resultString.begin()));
}
return resultString;
}
void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy)
{
m_ParseStrategy = parseStrategy;
}
void mitk::CustomTagParser::SetRevisionMappingStrategy(std::string revisionMappingStrategy)
{
m_RevisionMappingStrategy = revisionMappingStrategy;
}
std::string mitk::CustomTagParser::GetExternalJSONDirectory()
{
std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation();
std::string stringToModule;
std::string libraryName;
itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName);
std::stringstream jsonDirectory;
jsonDirectory << stringToModule << "/CESTRevisionMapping";
return jsonDirectory.str();
}
diff --git a/Modules/Chart/Test/mitkChartExampleTest.cpp b/Modules/Chart/Test/mitkChartExampleTest.cpp
index 689362b095..e411d9f95f 100644
--- a/Modules/Chart/Test/mitkChartExampleTest.cpp
+++ b/Modules/Chart/Test/mitkChartExampleTest.cpp
@@ -1,196 +1,196 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// qt
#include <QApplication>
// mitk chart
#include <mitkChartExampleTestHelper.h>
// mitk core
#include <mitkStandaloneDataStorage.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkPropertyNameHelper.h>
// std includes
#include <string>
class mitkChartExampleTestSuite : public mitk::TestFixture
{
QApplication a();
CPPUNIT_TEST_SUITE(mitkChartExampleTestSuite);
MITK_TEST(AddingDataTest);
MITK_TEST(CheckingDataTest);
MITK_TEST(ClearingDataTest);
CPPUNIT_TEST_SUITE_END();
private:
mitk::ChartExampleTestHelper* helper;
public:
void setUp() override
{
helper = new mitk::ChartExampleTestHelper;
}
void tearDown() override
{
delete helper;
}
void AddingDataTest()
{
MITK_INFO << "=== AddingDataTest start ===";
AddNewData();
CheckData();
ClearData();
MITK_INFO << "=== AddingDataTest end ===";
}
void CheckingDataTest()
{
MITK_INFO << "=== CheckingDataTest start ===";
CheckData();
MITK_INFO << "=== CheckingDataTest end ===";
}
void ClearingDataTest()
{
MITK_INFO << "=== ClearingDataTest start ===";
ClearData();
MITK_INFO << "=== ClearingDataTest end ===";
}
void AddNewData()
{
MITK_INFO << "=== AddNewData";
// Adding data
//size_t size;
for (size_t i = 1; i < 6; i++)
{
//helper.Add(i);
//size = helper.qmitkChartWidget.ReturnSizeOfMemory();
//CPPUNIT_ASSERT_MESSAGE("Data storage does not contain the right amount of items!", size == i);
}
}
void CheckData()
{
auto myDataOne = helper->GetDataOne();
auto xDataOne = myDataOne->GetXData();
auto yDataOne = myDataOne->GetYData();
auto labelOne = myDataOne->GetLabel();
auto typeOne = myDataOne->GetChartType();
auto colorOne = myDataOne->GetColor();
auto styleOne = myDataOne->GetLineStyle();
auto dataToCheckOne = helper->qmitkChartWidget.GetDataElementByLabel(labelOne.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset one was not saved correctly", dataToCheckOne->GetXData() == xDataOne &&
dataToCheckOne->GetYData() == yDataOne &&
dataToCheckOne->GetLabel() == labelOne &&
dataToCheckOne->GetChartType() == typeOne &&
dataToCheckOne->GetColor() == colorOne &&
dataToCheckOne->GetLineStyle() == styleOne);
auto myDataTwo = helper->GetDataTwo();
auto xDataTwo = myDataTwo->GetXData();
auto yDataTwo = myDataTwo->GetYData();
auto labelTwo = myDataTwo->GetLabel();
auto typeTwo = myDataTwo->GetChartType();
auto colorTwo = myDataTwo->GetColor();
auto styleTwo = myDataTwo->GetLineStyle();
auto dataToCheckTwo = helper->qmitkChartWidget.GetDataElementByLabel(labelTwo.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset two was not saved correctly", dataToCheckTwo->GetXData() == xDataTwo &&
dataToCheckTwo->GetYData() == yDataTwo &&
dataToCheckTwo->GetLabel() == labelTwo &&
dataToCheckTwo->GetChartType() == typeTwo &&
dataToCheckTwo->GetColor() == colorTwo &&
dataToCheckTwo->GetLineStyle() == styleTwo);
auto myDataThree = helper->GetDataThree();
auto xDataThree = myDataThree->GetXData();
auto yDataThree = myDataThree->GetYData();
auto labelThree = myDataThree->GetLabel();
auto typeThree = myDataThree->GetChartType();
auto colorThree = myDataThree->GetColor();
auto styleThree = myDataThree->GetLineStyle();
auto dataToCheckThree = helper->qmitkChartWidget.GetDataElementByLabel(labelThree.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset three was not saved correctly", dataToCheckThree->GetXData() == xDataThree &&
dataToCheckThree->GetYData() == yDataThree &&
dataToCheckThree->GetLabel() == labelThree &&
dataToCheckThree->GetChartType() == typeThree &&
dataToCheckThree->GetColor() == colorThree &&
dataToCheckThree->GetLineStyle() == styleThree);
auto myDataFour = helper->GetDataFour();
auto xDataFour = myDataFour->GetXData();
auto yDataFour = myDataFour->GetYData();
auto labelFour = myDataFour->GetLabel();
auto typeFour = myDataFour->GetChartType();
auto colorFour = myDataFour->GetColor();
auto styleFour = myDataFour->GetLineStyle();
auto dataToCheckFour = helper->qmitkChartWidget.GetDataElementByLabel(labelFour.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset one was not saved correctly", dataToCheckFour->GetXData() == xDataFour &&
dataToCheckFour->GetYData() == yDataFour &&
dataToCheckFour->GetLabel() == labelFour &&
dataToCheckFour->GetChartType() == typeFour &&
dataToCheckFour->GetColor() == colorFour &&
dataToCheckFour->GetLineStyle() == styleFour);
auto myDataFive = helper->GetDataFive();
auto xDataFive = myDataFive->GetXData();
auto yDataFive = myDataFive->GetYData();
auto labelFive = myDataFive->GetLabel();
auto typeFive = myDataFive->GetChartType();
auto colorFive = myDataFive->GetColor();
auto styleFive = myDataFive->GetLineStyle();
auto dataToCheckFive = helper->qmitkChartWidget.GetDataElementByLabel(labelFive.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset one was not saved correctly", dataToCheckFive->GetXData() == xDataFive &&
dataToCheckFive->GetYData() == yDataFive &&
dataToCheckFive->GetLabel() == labelFive &&
dataToCheckFive->GetChartType() == typeFive &&
dataToCheckFive->GetColor() == colorFive &&
dataToCheckFive->GetLineStyle() == styleFive);
}
void ClearData()
{
MITK_INFO << "=== ClearData";
// Claering data
mitk::ChartExampleTestHelper helper;
helper.qmitkChartWidget.Clear();
//int size = helper.qmitkChartWidget.ReturnSizeOfMemory();
- //CPPUNIT_ASSERT_MESSAGE("Data storage was not cleared completly!", size == 0);
+ //CPPUNIT_ASSERT_MESSAGE("Data storage was not cleared completely!", size == 0);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkChartExample)
diff --git a/Modules/Chart/src/QmitkChartWidget.cpp b/Modules/Chart/src/QmitkChartWidget.cpp
index 543dd4dfce..8110163093 100644
--- a/Modules/Chart/src/QmitkChartWidget.cpp
+++ b/Modules/Chart/src/QmitkChartWidget.cpp
@@ -1,940 +1,940 @@
/*============================================================================
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 <regex>
#include <QGridLayout>
#include <QWebChannel>
#include <QWebEngineSettings>
#include <QWebEngineView>
#include <QmitkChartWidget.h>
#include "mitkExceptionMacro.h"
#include <QmitkChartData.h>
#include <QmitkChartxyData.h>
class CustomPage : public QWebEnginePage
{
public:
CustomPage(QObject *parent = nullptr) : QWebEnginePage(parent) {}
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/,
const QString &message,
int lineNumber,
const QString & /*sourceID*/) override
{
MITK_INFO << "JS > " << lineNumber << ": " << message.toStdString();
}
};
class QmitkChartWidget::Impl final
{
public:
explicit Impl(QWidget *parent);
~Impl();
Impl(const Impl &) = delete;
Impl &operator=(const Impl &) = delete;
void AddData1D(const std::vector<double> &data1D, const std::string &label, QmitkChartWidget::ChartType chartType);
void AddData2D(const std::vector< std::pair<double, double> > &data2D,
const std::string &label,
QmitkChartWidget::ChartType chartType);
void AddChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& style,
const std::string& pieLabelsData = 0);
void UpdateData1D(const std::vector<double> &data1D, const std::string &label);
void UpdateData2D(const std::vector< std::pair<double, double> > &data2D, const std::string &label);
void UpdateChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData = 0);
void RemoveData(const std::string &label);
void UpdateLabel(const std::string &existingLabel, const std::string &newLabel);
QmitkChartxyData* GetDataElementByLabel(const std::string& label) const;
void ClearData();
void SetColor(const std::string &label, const std::string &colorName);
void SetLineStyle(const std::string &label, LineStyle style);
void SetMarkerSymbol(const std::string &label, MarkerSymbol symbol);
void SetYAxisScale(AxisScale scale);
void SetXAxisLabel(const std::string &label);
void SetYAxisLabel(const std::string &label);
void SetPieLabels(const std::vector<std::string> &pieLabels, const std::string &label);
void SetTitle(const std::string &title);
void SetXErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus = std::vector<double>());
void SetYErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus = std::vector<double>());
std::string GetThemeName() const;
void SetThemeName(ColorTheme style);
void SetLegendPosition(LegendPosition position);
void Show(bool showSubChart);
void SetShowLegend(bool show);
void SetShowErrorBars(bool show);
void SetStackedData(bool stacked);
void SetShowDataPoints(bool showDataPoints = false);
void SetShowSubchart(bool showSubChart);
void SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType);
void SetMinMaxValueXView(double minValueX, double maxValueX);
void SetMinMaxValueYView(double minValueY, double maxValueY);
QList<QVariant> ConvertErrorVectorToQList(const std::vector<double> &error);
QList<QVariant> ConvertVectorToQList(const std::vector<std::string> &vec);
std::string ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const;
void ClearJavaScriptChart();
void InitializeJavaScriptChart();
void CallJavaScriptFuntion(const QString &command);
QSize sizeHint() const;
void GetImageUrl();
private:
using ChartxyDataVector = std::vector<std::unique_ptr<QmitkChartxyData>>;
std::string GetUniqueLabelName(const QList<QVariant> &labelList, const std::string &label) const;
QList<QVariant> GetDataLabels(const ChartxyDataVector &c3xyData) const;
QWebChannel *m_WebChannel;
QWebEngineView *m_WebEngineView;
QmitkChartData m_C3Data;
ChartxyDataVector m_C3xyData;
std::map<QmitkChartWidget::ChartType, std::string> m_ChartTypeToName;
std::map<QmitkChartWidget::ChartColor, std::string> m_ChartColorToName;
std::map<QmitkChartWidget::ColorTheme, std::string> m_ColorThemeToName;
std::map<QmitkChartWidget::LegendPosition, std::string> m_LegendPositionToName;
std::map<QmitkChartWidget::LineStyle, std::string> m_LineStyleToName;
std::map<QmitkChartWidget::MarkerSymbol, std::string> m_MarkerSymbolToName;
std::map<QmitkChartWidget::AxisScale, std::string> m_AxisScaleToName;
};
QmitkChartWidget::Impl::Impl(QWidget *parent)
: m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent))
{
// disable context menu for QWebEngineView
m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu);
m_WebEngineView->setPage(new CustomPage());
// Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated.
m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html")));
m_WebEngineView->page()->setWebChannel(m_WebChannel);
m_WebEngineView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
- //This is added as a workarround for T28252 (https://phabricator.mitk.org/T28252)
+ //This is added as a workaround for T28252 (https://phabricator.mitk.org/T28252)
//can be removed if task is properly fixed.
m_WebEngineView->settings()->setAttribute(QWebEngineSettings::ShowScrollBars, false);
connect(m_WebEngineView, SIGNAL(loadFinished(bool)), parent, SLOT(OnLoadFinished(bool)));
auto layout = new QGridLayout(parent);
layout->setContentsMargins({});
layout->addWidget(m_WebEngineView);
m_ChartTypeToName.emplace(ChartType::bar, "bar");
m_ChartTypeToName.emplace(ChartType::line, "line");
m_ChartTypeToName.emplace(ChartType::spline, "spline");
m_ChartTypeToName.emplace(ChartType::pie, "pie");
m_ChartTypeToName.emplace(ChartType::area, "area");
m_ChartTypeToName.emplace(ChartType::area_spline, "area-spline");
m_ChartTypeToName.emplace(ChartType::scatter, "scatter");
m_ChartColorToName.emplace(ChartColor::red, "red");
m_ChartColorToName.emplace(ChartColor::orange, "orange");
m_ChartColorToName.emplace(ChartColor::yellow, "yellow");
m_ChartColorToName.emplace(ChartColor::green, "green");
m_ChartColorToName.emplace(ChartColor::blue, "blue");
m_ChartColorToName.emplace(ChartColor::purple, "purple");
m_ChartColorToName.emplace(ChartColor::brown, "brown");
m_ChartColorToName.emplace(ChartColor::magenta, "magenta");
m_ChartColorToName.emplace(ChartColor::tan, "tan");
m_ChartColorToName.emplace(ChartColor::cyan, "cyan");
m_ChartColorToName.emplace(ChartColor::olive, "olive");
m_ChartColorToName.emplace(ChartColor::maroon, "maroon");
m_ChartColorToName.emplace(ChartColor::navy, "navy");
m_ChartColorToName.emplace(ChartColor::aquamarine, "aquamarine");
m_ChartColorToName.emplace(ChartColor::turqouise, "turqouise");
m_ChartColorToName.emplace(ChartColor::silver, "silver");
m_ChartColorToName.emplace(ChartColor::lime, "lime");
m_ChartColorToName.emplace(ChartColor::teal, "teal");
m_ChartColorToName.emplace(ChartColor::indigo, "indigo");
m_ChartColorToName.emplace(ChartColor::violet, "violet");
m_ChartColorToName.emplace(ChartColor::pink, "pink");
m_ChartColorToName.emplace(ChartColor::black, "black");
m_ChartColorToName.emplace(ChartColor::white, "white");
m_ChartColorToName.emplace(ChartColor::grey, "grey");
m_LegendPositionToName.emplace(LegendPosition::bottomMiddle, "bottomMiddle");
m_LegendPositionToName.emplace(LegendPosition::bottomRight, "bottomRight");
m_LegendPositionToName.emplace(LegendPosition::topRight, "topRight");
m_LegendPositionToName.emplace(LegendPosition::topLeft, "topLeft");
m_LegendPositionToName.emplace(LegendPosition::middleRight, "middleRight");
m_LineStyleToName.emplace(LineStyle::solid, "solid");
m_LineStyleToName.emplace(LineStyle::dashed, "dashed");
m_MarkerSymbolToName.emplace(MarkerSymbol::circle, "circle");
m_MarkerSymbolToName.emplace(MarkerSymbol::cross, "cross");
m_MarkerSymbolToName.emplace(MarkerSymbol::diamond, "diamond");
m_MarkerSymbolToName.emplace(MarkerSymbol::pentagon, "pentagon");
m_MarkerSymbolToName.emplace(MarkerSymbol::square, "square");
m_MarkerSymbolToName.emplace(MarkerSymbol::star, "star");
m_MarkerSymbolToName.emplace(MarkerSymbol::x, "x");
m_MarkerSymbolToName.emplace(MarkerSymbol::diamond_tall, "diamond-tall");
m_MarkerSymbolToName.emplace(MarkerSymbol::star_diamond, "star-diamond");
m_MarkerSymbolToName.emplace(MarkerSymbol::star_triangle_up, "star-triangle-up");
m_MarkerSymbolToName.emplace(MarkerSymbol::star_triangle_down, "star-triangle-down");
m_MarkerSymbolToName.emplace(MarkerSymbol::asterisk, "asterisk");
m_MarkerSymbolToName.emplace(MarkerSymbol::cross_thin, "cross-thin");
m_MarkerSymbolToName.emplace(MarkerSymbol::x_thin, "x-thin");
m_AxisScaleToName.emplace(AxisScale::linear, "");
m_AxisScaleToName.emplace(AxisScale::log, "log");
m_ColorThemeToName.emplace(ColorTheme::lightstyle, "light");
m_ColorThemeToName.emplace(ColorTheme::darkstyle, "dark");
}
QmitkChartWidget::Impl::~Impl() {}
std::string QmitkChartWidget::Impl::GetThemeName() const
{
return m_C3Data.GetThemeName().toString().toStdString();
}
std::string CheckForCorrectHex(const std::string &colorName)
{
std::regex rgx("([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})");
std::smatch match;
if (!colorName.empty() && colorName.at(0) != '#' && std::regex_search(colorName.begin(), colorName.end(), match, rgx))
{
return "#" + colorName;
}
else
{
return colorName;
}
}
void QmitkChartWidget::Impl::GetImageUrl()
{
m_C3Data.EmitSignalImageUrl();
}
void QmitkChartWidget::Impl::AddData1D(const std::vector<double> &data1D,
const std::string &label,
QmitkChartWidget::ChartType type)
{
std::vector< std::pair<double, double> > transformedData2D;
unsigned int count = 0;
// transform the 1D data to 2D data
for (const auto &ele : data1D)
{
transformedData2D.emplace_back(count, ele);
count++;
}
AddData2D(transformedData2D, label, type);
}
void QmitkChartWidget::Impl::AddData2D(const std::vector< std::pair<double, double> > &data2D,
const std::string &label,
QmitkChartWidget::ChartType type)
{
const std::string chartTypeName(m_ChartTypeToName.at(type));
auto definedLabels = GetDataLabels(m_C3xyData);
auto uniqueLabel = GetUniqueLabelName(definedLabels, label);
unsigned int sizeOfC3xyData = static_cast<unsigned int>(m_C3xyData.size());
m_C3xyData.push_back(std::make_unique<QmitkChartxyData>(data2D,
QVariant(QString::fromStdString(uniqueLabel)),
QVariant(QString::fromStdString(chartTypeName)),
QVariant(sizeOfC3xyData)));
}
void QmitkChartWidget::Impl::AddChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
auto definedLabels = GetDataLabels(m_C3xyData);
auto uniqueLabel = GetUniqueLabelName(definedLabels, label);
if (type == "scatter")
{
SetShowDataPoints(true);
MITK_INFO << "Enabling data points for all because of scatter plot";
}
unsigned int sizeOfC3xyData = static_cast<unsigned int>(m_C3xyData.size());
std::unique_ptr<QmitkChartxyData> chartData =
std::make_unique<QmitkChartxyData>(
data2D,
QVariant(QString::fromStdString(uniqueLabel)),
QVariant(QString::fromStdString(type)),
QVariant(sizeOfC3xyData));
chartData->SetColor(QVariant(QString::fromStdString(color)));
chartData->SetLineStyle(QVariant(QString::fromStdString(lineStyle)));
if (pieLabelsData != "")
{
std::string pieLabelsDataWorkingString = pieLabelsData;
QList<QVariant> pieLabelsDataList;
while (pieLabelsDataWorkingString.size() != 0)
{
QVariant oneElement = QString::fromStdString(pieLabelsDataWorkingString.substr(0, pieLabelsDataWorkingString.find(";")));
pieLabelsDataList.push_back(oneElement);
if (pieLabelsDataWorkingString.find(";") != std::string::npos)
{
pieLabelsDataWorkingString.erase(0, pieLabelsDataWorkingString.find(";") + 1);
}
else
{
pieLabelsDataWorkingString.erase(pieLabelsDataWorkingString.begin(), pieLabelsDataWorkingString.end());
}
}
chartData->SetPieLabels(pieLabelsDataList);
}
m_C3xyData.push_back(std::move(chartData));
}
void QmitkChartWidget::Impl::UpdateData1D(const std::vector<double> &data1D, const std::string &label)
{
std::vector< std::pair<double, double> > transformedData2D;
unsigned int count = 0;
// transform the 1D data to 2D data
for (const auto &ele : data1D)
{
transformedData2D.emplace_back( count, ele );
count++;
}
UpdateData2D(transformedData2D, label);
}
void QmitkChartWidget::Impl::UpdateData2D(const std::vector< std::pair<double, double> > &data2D, const std::string &label)
{
auto element = GetDataElementByLabel(label);
if (element)
element->SetData(data2D);
}
void QmitkChartWidget::Impl::UpdateChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
UpdateData2D(data2D, label);
auto element = GetDataElementByLabel(label);
if (element)
{
element->SetChartType(QString::fromStdString(type));
element->SetColor(QString::fromStdString(color));
element->SetLineStyle(QString::fromStdString(lineStyle));
if (pieLabelsData != "")
{
std::string pieLabelsDataWorkingString = pieLabelsData;
QList<QVariant> pieLabelsDataList;
while (pieLabelsDataWorkingString.size() != 0)
{
QVariant oneElement = QString::fromStdString(pieLabelsDataWorkingString.substr(0, pieLabelsDataWorkingString.find(";")));
pieLabelsDataList.push_back(oneElement);
if (pieLabelsDataWorkingString.find(";") != std::string::npos)
{
pieLabelsDataWorkingString.erase(0, pieLabelsDataWorkingString.find(";") + 1);
}
else
{
pieLabelsDataWorkingString.erase(pieLabelsDataWorkingString.begin(), pieLabelsDataWorkingString.end());
}
}
element->SetPieLabels(pieLabelsDataList);
}
}
}
void QmitkChartWidget::Impl::RemoveData(const std::string &label)
{
for (ChartxyDataVector::iterator iter = m_C3xyData.begin(); iter != m_C3xyData.end(); ++iter)
{
if ((*iter)->GetLabel().toString().toStdString() == label)
{
m_C3xyData.erase(iter);
return;
}
}
throw std::invalid_argument("Cannot Remove Data because the label does not exist.");
}
void QmitkChartWidget::Impl::ClearData()
{
for (auto &xyData : m_C3xyData)
{
m_WebChannel->deregisterObject(xyData.get());
}
m_C3xyData.clear();
}
void QmitkChartWidget::Impl::UpdateLabel(const std::string &existingLabel, const std::string &newLabel) {
auto element = GetDataElementByLabel(existingLabel);
if (element)
{
auto definedLabels = GetDataLabels(m_C3xyData);
auto uniqueLabel = GetUniqueLabelName(definedLabels, newLabel);
element->SetLabel(QString::fromStdString(uniqueLabel));
}
}
void QmitkChartWidget::Impl::SetColor(const std::string &label, const std::string &colorName)
{
auto element = GetDataElementByLabel(label);
if (element)
{
auto colorChecked = CheckForCorrectHex(colorName);
element->SetColor(QVariant(QString::fromStdString(colorChecked)));
}
}
void QmitkChartWidget::Impl::SetLineStyle(const std::string &label, LineStyle style)
{
auto element = GetDataElementByLabel(label);
const std::string lineStyleName(m_LineStyleToName.at(style));
element->SetLineStyle(QVariant(QString::fromStdString(lineStyleName)));
}
void QmitkChartWidget::Impl::SetMarkerSymbol(const std::string &label, MarkerSymbol symbol)
{
auto element = GetDataElementByLabel(label);
const std::string markerSymbolName(m_MarkerSymbolToName.at(symbol));
element->SetMarkerSymbol(QVariant(QString::fromStdString(markerSymbolName)));
}
void QmitkChartWidget::Impl::SetYAxisScale(AxisScale scale)
{
const std::string axisScaleName(m_AxisScaleToName.at(scale));
m_C3Data.SetYAxisScale(QString::fromStdString(axisScaleName));
}
QmitkChartxyData *QmitkChartWidget::Impl::GetDataElementByLabel(const std::string &label) const
{
for (const auto &qmitkChartxyData : m_C3xyData)
{
if (qmitkChartxyData->GetLabel().toString() == label.c_str())
{
return qmitkChartxyData.get();
}
}
return nullptr;
}
QList<QVariant> QmitkChartWidget::Impl::GetDataLabels(const ChartxyDataVector &c3xyData) const
{
QList<QVariant> dataLabels;
for (auto element = c3xyData.begin(); element != c3xyData.end(); ++element)
{
dataLabels.push_back((*element)->GetLabel());
}
return dataLabels;
}
void QmitkChartWidget::Impl::SetXAxisLabel(const std::string &label)
{
m_C3Data.SetXAxisLabel(QString::fromStdString(label));
}
void QmitkChartWidget::Impl::SetYAxisLabel(const std::string &label)
{
m_C3Data.SetYAxisLabel(QString::fromStdString(label));
}
void QmitkChartWidget::Impl::SetPieLabels(const std::vector<std::string> &pieLabels, const std::string &label)
{
auto element = GetDataElementByLabel(label);
if (element)
{
if (element->GetChartType() == QVariant("pie"))
{
auto dataY = element->GetYData();
element->SetPieLabels(ConvertVectorToQList(pieLabels));
if (static_cast<unsigned>(dataY.size()) != pieLabels.size())
{
MITK_INFO << "data has " << dataY.size() << " entries whereas pie labels have " << pieLabels.size()
<< " entries. Unnamed pie labels automatically get a numerical label.";
}
}
else
{
MITK_INFO << "label" << label << "has chart type " << element->GetChartType().toString().toStdString() << ", but pie is required";
}
}
}
void QmitkChartWidget::Impl::SetTitle(const std::string &title)
{
m_C3Data.SetTitle(QString::fromStdString(title));
}
void QmitkChartWidget::Impl::SetThemeName(QmitkChartWidget::ColorTheme style)
{
const std::string themeName(m_ColorThemeToName.at(style));
m_C3Data.SetThemeName(QString::fromStdString(themeName));
}
void QmitkChartWidget::Impl::SetLegendPosition(QmitkChartWidget::LegendPosition legendPosition)
{
const std::string legendPositionName(m_LegendPositionToName.at(legendPosition));
m_C3Data.SetLegendPosition(QString::fromStdString(legendPositionName));
}
void QmitkChartWidget::Impl::Show(bool showSubChart)
{
if (m_C3xyData.empty())
{
MITK_WARN << "no data available for display in chart";
}
else
{
m_C3Data.SetAppearance(showSubChart, m_C3xyData.front()->GetChartType() == QVariant("pie"));
}
InitializeJavaScriptChart();
}
void QmitkChartWidget::Impl::SetShowLegend(bool show)
{
m_C3Data.SetShowLegend(show);
}
void QmitkChartWidget::Impl::SetStackedData(bool stacked)
{
m_C3Data.SetStackedData(stacked);
}
void QmitkChartWidget::Impl::SetShowErrorBars(bool show)
{
m_C3Data.SetShowErrorBars(show);
}
void QmitkChartWidget::Impl::SetShowDataPoints(bool showDataPoints)
{
if (showDataPoints == true)
{
m_C3Data.SetDataPointSize(6.5);
}
else
{
m_C3Data.SetDataPointSize(0);
}
}
void QmitkChartWidget::Impl::SetShowSubchart(bool showSubChart) {
m_C3Data.SetShowSubchart(showSubChart);
}
void QmitkChartWidget::Impl::SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType)
{
auto element = GetDataElementByLabel(label);
if (element)
{
if (chartType == ChartType::scatter)
{
SetShowDataPoints(true);
MITK_INFO << "Enabling data points for all because of scatter plot";
}
const std::string chartTypeName(m_ChartTypeToName.at(chartType));
element->SetChartType(QVariant(QString::fromStdString(chartTypeName)));
}
}
void QmitkChartWidget::Impl::SetMinMaxValueXView(double minValueX, double maxValueX) {
m_C3Data.SetMinValueXView(minValueX);
m_C3Data.SetMaxValueXView(maxValueX);
}
void QmitkChartWidget::Impl::SetMinMaxValueYView(double minValueY, double maxValueY) {
m_C3Data.SetMinValueYView(minValueY);
m_C3Data.SetMaxValueYView(maxValueY);
}
QList<QVariant> QmitkChartWidget::Impl::ConvertErrorVectorToQList(const std::vector<double> &error)
{
QList<QVariant> errorConverted;
for (const auto &aValue : error)
{
errorConverted.append(aValue);
}
return errorConverted;
}
QList<QVariant> QmitkChartWidget::Impl::ConvertVectorToQList(const std::vector<std::string> &vec)
{
QList<QVariant> vecConverted;
for (const auto &aValue : vec)
{
vecConverted.append(QString::fromStdString(aValue));
}
return vecConverted;
}
void QmitkChartWidget::Impl::SetXErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
auto element = GetDataElementByLabel(label);
if (element)
{
auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus);
auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus);
element->SetXErrorDataPlus(errorConvertedPlus);
element->SetXErrorDataMinus(errorConvertedMinus);
}
}
void QmitkChartWidget::Impl::SetYErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
auto element = GetDataElementByLabel(label);
if (element)
{
auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus);
auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus);
element->SetYErrorDataPlus(errorConvertedPlus);
element->SetYErrorDataMinus(errorConvertedMinus);
}
}
std::string QmitkChartWidget::Impl::ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const
{
return m_ChartTypeToName.at(chartType);
}
QSize QmitkChartWidget::Impl::sizeHint() const
{
return QSize(400, 300);
}
void QmitkChartWidget::Impl::CallJavaScriptFuntion(const QString &command)
{
m_WebEngineView->page()->runJavaScript(command);
}
void QmitkChartWidget::Impl::ClearJavaScriptChart()
{
m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html")));
}
void QmitkChartWidget::Impl::InitializeJavaScriptChart()
{
auto alreadyRegisteredObjects = m_WebChannel->registeredObjects();
auto alreadyRegisteredObjectsValues = alreadyRegisteredObjects.values();
// only register objects that have not been registered yet
if (alreadyRegisteredObjectsValues.indexOf(&m_C3Data) == -1)
{
m_WebChannel->registerObject(QStringLiteral("chartData"), &m_C3Data);
}
unsigned count = 0;
for (auto &xyData : m_C3xyData)
{
// only register objects that have not been registered yet
if (alreadyRegisteredObjectsValues.indexOf(xyData.get()) == -1)
{
QString variableName = "xyData" + QString::number(count);
m_WebChannel->registerObject(variableName, xyData.get());
}
count++;
}
m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/QmitkChartWidget.html")));
}
std::string QmitkChartWidget::Impl::GetUniqueLabelName(const QList<QVariant> &labelList, const std::string &label) const
{
QString currentLabel = QString::fromStdString(label);
int counter = 0;
while (labelList.contains(currentLabel))
{
currentLabel = QString::fromStdString(label + std::to_string(counter));
counter++;
}
return currentLabel.toStdString();
}
QmitkChartWidget::QmitkChartWidget(QWidget *parent) : QWidget(parent), m_Impl(new Impl(this))
{
connect(this, &QmitkChartWidget::PageSuccessfullyLoaded, this, &QmitkChartWidget::OnPageSuccessfullyLoaded);
}
QmitkChartWidget::~QmitkChartWidget() {}
void QmitkChartWidget::SetColor(const std::string &label, const std::string &colorName)
{
m_Impl->SetColor(label, colorName);
}
void QmitkChartWidget::SetLineStyle(const std::string &label, LineStyle style)
{
m_Impl->SetLineStyle(label, style);
}
void QmitkChartWidget::SetMarkerSymbol(const std::string &label, MarkerSymbol symbol)
{
m_Impl->SetMarkerSymbol(label, symbol);
}
void QmitkChartWidget::SetYAxisScale(AxisScale scale)
{
m_Impl->SetYAxisScale(scale);
}
void QmitkChartWidget::AddData1D(const std::vector<double> &data1D, const std::string &label, ChartType type)
{
m_Impl->AddData1D(data1D, label, type);
}
void QmitkChartWidget::AddData2D(const std::vector< std::pair<double, double> >& data2D, const std::string& label, ChartType type)
{
m_Impl->AddData2D(data2D, label, type);
}
void QmitkChartWidget::AddChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
m_Impl->AddChartExampleData(data2D, label, type, color, lineStyle, pieLabelsData);
}
void QmitkChartWidget::UpdateData1D(const std::vector<double> &data1D, const std::string &label)
{
m_Impl->UpdateData1D(data1D, label);
}
void QmitkChartWidget::UpdateData2D(const std::vector< std::pair<double, double> > &data2D, const std::string &label)
{
m_Impl->UpdateData2D(data2D, label);
}
void QmitkChartWidget::UpdateChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
m_Impl->UpdateChartExampleData(data2D, label, type, color, lineStyle, pieLabelsData);
}
void QmitkChartWidget::RemoveData(const std::string &label)
{
m_Impl->RemoveData(label);
}
void QmitkChartWidget::UpdateLabel(const std::string &existingLabel, const std::string &newLabel) {
m_Impl->UpdateLabel(existingLabel, newLabel);
}
QmitkChartxyData* QmitkChartWidget::GetDataElementByLabel(const std::string& label) const
{
return m_Impl->GetDataElementByLabel(label);
}
void QmitkChartWidget::SetXAxisLabel(const std::string &label)
{
m_Impl->SetXAxisLabel(label);
}
void QmitkChartWidget::SetYAxisLabel(const std::string &label)
{
m_Impl->SetYAxisLabel(label);
}
void QmitkChartWidget::SetPieLabels(const std::vector<std::string> &pieLabels, const std::string &label)
{
m_Impl->SetPieLabels(pieLabels, label);
}
void QmitkChartWidget::SetTitle(const std::string &title)
{
m_Impl->SetTitle(title);
}
void QmitkChartWidget::SetShowDataPoints(bool showDataPoints)
{
m_Impl->SetShowDataPoints(showDataPoints);
}
void QmitkChartWidget::SetChartType(const std::string &label, ChartType type)
{
m_Impl->SetChartType(label, type);
}
void QmitkChartWidget::SetXErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
m_Impl->SetXErrorBars(label, errorPlus, errorMinus);
}
void QmitkChartWidget::SetYErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
m_Impl->SetYErrorBars(label, errorPlus, errorMinus);
}
void QmitkChartWidget::SetLegendPosition(LegendPosition position)
{
m_Impl->SetLegendPosition(position);
}
void QmitkChartWidget::SetShowLegend(bool show)
{
m_Impl->SetShowLegend(show);
}
void QmitkChartWidget::SetStackedData(bool stacked)
{
m_Impl->SetStackedData(stacked);
}
void QmitkChartWidget::Show(bool showSubChart)
{
m_Impl->Show(showSubChart);
}
void QmitkChartWidget::Clear()
{
m_Impl->ClearData();
m_Impl->ClearJavaScriptChart();
}
void QmitkChartWidget::OnLoadFinished(bool isLoadSuccessful)
{
if (isLoadSuccessful)
{
emit PageSuccessfullyLoaded();
}
}
void QmitkChartWidget::OnPageSuccessfullyLoaded()
{
auto themeName = m_Impl->GetThemeName();
QString command;
if (themeName == "dark")
{
command = QString("changeTheme('dark')");
}
else
{
command = QString("changeTheme('light')");
}
m_Impl->CallJavaScriptFuntion(command);
}
void QmitkChartWidget::SetTheme(ColorTheme themeEnabled)
{
m_Impl->SetThemeName(themeEnabled);
}
void QmitkChartWidget::SetShowSubchart(bool showSubChart)
{
m_Impl->SetShowSubchart(showSubChart);
}
void QmitkChartWidget::SetShowErrorBars(bool showErrorBars)
{
m_Impl->SetShowErrorBars(showErrorBars);
}
void QmitkChartWidget::SetMinMaxValueXView(double minValueX, double maxValueX)
{
m_Impl->SetMinMaxValueXView(minValueX, maxValueX);
}
void QmitkChartWidget::SetMinMaxValueYView(double minValueY, double maxValueY)
{
m_Impl->SetMinMaxValueYView(minValueY, maxValueY);
}
void QmitkChartWidget::Reload()
{
const QString command = QString("Reload()");
m_Impl->CallJavaScriptFuntion(command);
}
QSize QmitkChartWidget::sizeHint() const
{
return m_Impl->sizeHint();
}
void QmitkChartWidget::SavePlotAsImage()
{
m_Impl->GetImageUrl();
}
diff --git a/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h b/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h
index 623d79c799..5f3234358e 100644
--- a/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h
+++ b/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h
@@ -1,342 +1,342 @@
/*============================================================================
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 mitkAbstractGlobalImageFeature_h
#define mitkAbstractGlobalImageFeature_h
#include <MitkCLCoreExports.h>
#include <mitkBaseData.h>
#include <mitkImage.h>
#include <mitkCommandLineParser.h>
#include <mitkIntensityQuantifier.h>
// STD Includes
// Eigen
#include <itkeigen/Eigen/Dense>
// MITK includes
#include <mitkConfigurationHolder.h>
namespace mitk
{
/**Used as ID for features calculated by feature classes*/
struct MITKCLCORE_EXPORT FeatureID
{
/**Name of the feature*/
std::string name;
/**Name of the feature class*/
std::string featureClass;
/**ID for the setting that is represented by parameters and is specified by the feature class while calculating the features. It must be as unique as the parameters themself.*/
std::string settingID;
- /**Alternative name that containes the legacy naming of the feature that encodes the parametersetting directly in the string.*/
+ /**Alternative name that contains the legacy naming of the feature that encodes the parametersetting directly in the string.*/
std::string legacyName;
/**Version of the feature definition*/
std::string version = "1";
using ParametersType = std::map<std::string, us::Any>;
ParametersType parameters;
bool operator < (const FeatureID& rh) const;
bool operator ==(const FeatureID& rh) const;
};
- /**Helper that takes a pass templateID clones it and populates it with the also passed informations befor returning it.
+ /**Helper that takes a pass templateID clones it and populates it with the also passed informations before returning it.
* @param templateID reference ID that should be cloned.
* @param name Name of the feature.*/
MITKCLCORE_EXPORT FeatureID CreateFeatureID(FeatureID templateID, std::string name);
/**
*
*
* ## Histogram Configuration ##
* Most Feature Generation Classes that use histograms use the same parameters and
* initialization logic. In general, all information can be passed either by the corresponding
- * Setter (which does not differenciate between global setting and feature specific setting) and
+ * Setter (which does not differentiate between global setting and feature specific setting) and
* a parameter object which can be obtained from the command line arguments, for example.
*
- * If the image values are used for the initializiation of the histogram, it can be defined
+ * If the image values are used for the initialization of the histogram, it can be defined
* whether the whole image is used or only the masked areas to find minima and maxima. This is
- * done by the option <b>SetIgnoreMask</b> or the corrsponding options
+ * done by the option <b>SetIgnoreMask</b> or the corresponding options
* <b>-%NAME::ignore-mask-for-histogram</b> and <b>-ignore-mask-for-histogram</b>. If these are
* true, the whole image is used for the calculation.
*
* Depending on the passed arguments, different initialization methods are used. The initialization
* is in the following order:
* - If <b>Minimum Intensity</b>, <b>Maximum Intensity</b>, and <b>Binsize</b>: The histogram is
* initialized between the minimum and maximum intensity. the number of bins is determined by the
* binsize. If the distance between minimum and maximum is not a multiple of the binsize, the maximum
* is increase so that it is.
* - <b>Minimum Intensity</b>, <b>Bins</b>, and <b>Binsize</b>: The histogram is initialized with the
* given binsize, and the intensity range from the minimum to \f$maximum = minimum + binsize*bins\f$.
* - <b>Minimum Intensity</b>, <b>Maximum Intensity</b>, and <b>Bins</b>: The histogram is initialized
* between the given minimum and maximum intensity. The binsize is calculated so that the number
* of bins is equal to the given number of bins.
* - <b>Binsize</b>, and <b>Minimum Intensity</b>: The maximum is set to the maximum that
* occur in the given image. Depending if the mask is considered or not, either only masked voxels or
* the whole image is used for the calculation. The initialization is then equal as if the minimum
* and maximum would have been given right from the beginning.
* - <b>Binsize</b>, and <b>Maximum Intensity</b>: The minimum intensity is set to the minimum that
* occur in the given image. Depending if the mask is considered or not, either only masked voxels or
* the whole image is used for the calculation. The initialization is then equal as if the minimum
* and maximum would have been given right from the beginning.
* - <b>Binsize</b>: The maximum and the minimum intensity is set to the minimum and maximum that
* occur in the given image. Depending if the mask is considered or not, either only masked voxels or
* the whole image is used for the calculation. The initialization is then equal as if the minimum
* and maximum would have been given right from the beginning.
* - <b>Bins</b>, and <b>Minimum Intensity</b>: The maximum is calculated from the image. Depending
* if the mask is considered or not, either only masked voxels or the whole image is used for the calculation. The histogram is
* then initialized as if these values would have been given as minimum and maximum intensity.
* - <b>Bins</b>, and <b>Maximum Intensity</b>: The minimum is calculated from the image. Depending
* if the mask is considered or not, either only masked voxels or the whole image is used for the calculation. The histogram is
* then initialized as if these values would have been given as minimum and maximum intensity.
* - <b>Bins</b>: The minimum and the maximum is calculated from the image. Depending
* if the mask is considered or not, either only masked voxels or * the whole image is used for the calculation. The histogram is
* then initialized as if these values would have been given as minimum and maximum intensity.
* - <b>No Parameter given</b>:The minimum and maximum intensity from the whole image or masked image is calculated and
* the histogram then initialized to this with a standard number of bins (Is set by each filter on its own.)
*
* ### Remark about command line parameter####
* There are generally two options to set a parameter via the command line. A global one that works for
* all filters that use histograms and a local one that set this parameter specific for this filter. The
- * local parameters start with the filter name (Indiciated by NAME) followed by two colons, for example
+ * local parameters start with the filter name (Indicated by NAME) followed by two colons, for example
* <b>vol::min</b> to set the minimum intensity for the volume filter. The global parameter is overwritten
* by the local parameter, if it is specified. Otherwise, it is still valid. If this prevents the specification
* of an histogram initialization method (for example, because the binsize is globally specified but the histogram
- * should be initialized using a fixed numbe of bins), the parameter <b>NAME::ignore-global-histogram</b> can be passed.
+ * should be initialized using a fixed number of bins), the parameter <b>NAME::ignore-global-histogram</b> can be passed.
* Then, all global histogram parameters are ignored and only local ones are used.
*
* The maximum intensity can be set by different command line parameters: global for all filters that use histograms
* by <b>-minimum-intensity</b> and <b>-minimum</b>. Alternative it can be set only for this filter by
* <b>-%NAME::minimum</b> and <b>-%NAME::min</b>.
*
* The minimum intensity can be set by different command line parameters: global for all filters that use histograms
* by <b>-maximum-intensity</b> and <b>-maximum</b>. Alternative it can be set only for this filter by
* <b>-%NAME::maximum</b> and <b>-%NAME::max</b>.
*
* The binsize can be set by different command line parameters: global for all filters that use histograms
* by <b>-binsize</b>. Alternative it can be set only for this filter by
* <b>-%NAME::binsize</b>.
*
* The number of bins can be set by different command line parameters: global for all filters that use histograms
* by <b>-bins</b>. Alternative it can be set only for this filter by
* <b>-%NAME::bins</b>.
* ### Note to the developers ###
* All features are supposed to work the same way if a histogram is used somewhere in
* the code. For this, each derived class that makes use of a histogram should use
* the Quantifier object. In order to use this object correctly, the AddArguments-Function should
* contain the line <b>AddQuantifierArguments(parser);</b>, the CalculateFeaturesUsingParameters function
* should contain the line <b>InitializeQuantifierFromParameters(feature, mask);</b> and the CalculateFeatures function
- * sould contain the line <b>InitializeQuantifier(image, mask);</b>. These function
+ * should contain the line <b>InitializeQuantifier(image, mask);</b>. These function
* calls ensure that the necessary options are given to the configuration file, and that the initialization
- * of the quantifier is done correctly. This ensures an consistend behavior over all FeatureGeneration Classes.
+ * of the quantifier is done correctly. This ensures a consistent behavior over all FeatureGeneration Classes.
*
*/
class MITKCLCORE_EXPORT AbstractGlobalImageFeature : public BaseData
{
public:
mitkClassMacro(AbstractGlobalImageFeature, BaseData);
typedef std::vector< std::pair<FeatureID, double> > FeatureListType;
using ParametersType = FeatureID::ParametersType;
/**
- * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings.
+ * \brief Calculates the feature of this abstract interface. Does not necessarily considers the parameter settings.
*/
FeatureListType CalculateFeatures(const Image* image, const Image* mask);
virtual FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) = 0;
/**
- * \brief Calculates the given feature Slice-wise. Might not be availble for an individual filter!
+ * \brief Calculates the given feature Slice-wise. Might not be available for an individual filter!
*/
FeatureListType CalculateFeaturesSlicewise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID);
/**
- * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings.
+ * \brief Calculates the feature of this abstract interface. Does not necessarily considers the parameter settings.
*/
virtual void CalculateAndAppendFeaturesSliceWise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID, FeatureListType &featureList, bool checkParameterActivation = true);
/**
- * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings.
+ * \brief Calculates the feature of this abstract interface. Does not necessarily considers the parameter settings.
* @param image
* @param mask
* @param maskNoNaN
* @param featureList
* @param checkParameterActivation Indicates if the features should only be calculated and added if the FeatureClass is activated in the parameters.
- * True: only append if activated in the parametes. False: always and append it.
+ * True: only append if activated in the parameters. False: always and append it.
*/
void CalculateAndAppendFeatures(const Image* image, const Image* mask, const Image* maskNoNaN, FeatureListType &featureList, bool checkParameterActivation = true);
itkSetMacro(Prefix, std::string);
itkSetMacro(ShortName, std::string);
itkSetMacro(LongName, std::string);
itkSetMacro(FeatureClassName, std::string);
itkSetMacro(Direction, int);
void SetParameters(ParametersType param)
{
m_Parameters = param;
this->ConfigureQuantifierSettingsByParameters();
this->ConfigureSettingsByParameters(param);
this->Modified();
};
itkGetConstMacro(Prefix, std::string);
itkGetConstMacro(ShortName, std::string);
itkGetConstMacro(LongName, std::string);
itkGetConstMacro(FeatureClassName, std::string);
itkGetConstMacro(Parameters, ParametersType);
itkGetMacro(Quantifier, IntensityQuantifier::Pointer);
itkGetConstMacro(Direction, int);
itkSetMacro(MinimumIntensity, double);
itkSetMacro(UseMinimumIntensity, bool);
itkSetMacro(MaximumIntensity, double);
itkSetMacro(UseMaximumIntensity, bool);
itkGetConstMacro(MinimumIntensity, double);
itkGetConstMacro(UseMinimumIntensity, bool);
itkGetConstMacro(MaximumIntensity, double);
itkGetConstMacro(UseMaximumIntensity, bool);
itkSetMacro(Binsize, double);
itkSetMacro(UseBinsize, bool);
itkGetConstMacro(Binsize, double);
itkGetConstMacro(UseBinsize, bool);
itkSetMacro(MorphMask, mitk::Image::Pointer);
itkGetConstMacro(MorphMask, mitk::Image::Pointer);
itkSetMacro(Bins, int);
itkSetMacro(UseBins, bool);
itkGetConstMacro(UseBins, bool);
itkGetConstMacro(Bins, int);
itkSetMacro(IgnoreMask, bool);
itkGetConstMacro(IgnoreMask, bool);
itkSetMacro(EncodeParametersInFeaturePrefix, bool);
itkGetConstMacro(EncodeParametersInFeaturePrefix, bool);
itkBooleanMacro(EncodeParametersInFeaturePrefix);
std::string GetOptionPrefix() const
{
if (!m_Prefix.empty())
return m_Prefix + "::" + m_ShortName;
return m_ShortName;
}
/** Can be called to add all relevant argument for configuring the feature instance to the passed parser instance.
Must be implemented be derived classes. For adding the quantifier arguments use AddQuantifierArguments(...) as
helper function.*/
virtual void AddArguments(mitkCommandLineParser &parser) const = 0;
/** Helper function that generates the legacy feature name without encoding of parameters; as it is used e.g.
in the unit tests.*/
static std::string GenerateLegacyFeatureNameWOEncoding(const FeatureID& id);
protected:
std::vector<double> SplitDouble(std::string str, char delimiter);
virtual FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) = 0;
void AddQuantifierArguments(mitkCommandLineParser& parser) const;
/** Ensures that all quantifier relevant variables of the instance are set correctly given the information in m_Parameters.*/
void ConfigureQuantifierSettingsByParameters();
/** Ensures that the instance is configured according to the information given in the passed parameters.
* This method will be called by SetParameters(...) after ConfigureQuantifierSettingsByParameters() was called.*/
virtual void ConfigureSettingsByParameters(const ParametersType& parameters);
/**Initializes the quantifier gigen the quantifier relevant variables and the passed arguments.*/
void InitializeQuantifier(const Image* image, const Image* mask, unsigned int defaultBins = 256);
/** Helper that encodes the quantifier parameters in a string (e.g. used for the legacy feature name)*/
std::string QuantifierParameterString() const;
/* Creates a template feature id.
* it will set the featureClass, the settingID (assuming that it is the featureClass with the passed suffix
* and all parameters that are global or have the option prefix of the instance.*/
FeatureID CreateTemplateFeatureID(std::string settingsSuffix = "", FeatureID::ParametersType additionalParams = {});
/** Helper that generates the legacy feature names for a passed FeatureID.
* Format of the legacy feature name is: \<ClassName\>::[\<LegacyFeatureEncoding\>::]\<LegacyFeatureNamePart\>
* Overwrite GenerateLegacyFeatureNamePart and GenerateLegacyFeatureEncoding to change behavior in
* derived classes.
*/
virtual std::string GenerateLegacyFeatureName(const FeatureID& id) const;
virtual std::string GenerateLegacyFeatureNamePart(const FeatureID& id) const;
virtual std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const;
public:
//#ifndef DOXYGEN_SKIP
void SetRequestedRegionToLargestPossibleRegion() override {};
bool RequestedRegionIsOutsideOfTheBufferedRegion() override { return true; };
bool VerifyRequestedRegion() override { return false; };
void SetRequestedRegion (const itk::DataObject * /*data*/) override {};
// Override
bool IsEmpty() const override
{
if(IsInitialized() == false)
return true;
const TimeGeometry* timeGeometry = const_cast<AbstractGlobalImageFeature*>(this)->GetUpdatedTimeGeometry();
if(timeGeometry == nullptr)
return true;
return false;
}
private:
std::string m_Prefix; // Prefix before all input parameters
std::string m_ShortName; // Name of all variables
std::string m_LongName; // Long version of the name (For turning on)
std::string m_FeatureClassName;
ParametersType m_Parameters; // Parameter setting
mitk::Image::Pointer m_MorphMask = nullptr;
IntensityQuantifier::Pointer m_Quantifier;
//Quantifier relevant variables
double m_MinimumIntensity = 0;
bool m_UseMinimumIntensity = false;
double m_MaximumIntensity = 100;
bool m_UseMaximumIntensity = false;
bool m_EncodeParametersInFeaturePrefix = false;
double m_Binsize = 1;
bool m_UseBinsize = false;
int m_Bins = 256;
bool m_UseBins = true;
int m_Direction = 0;
bool m_IgnoreMask = false;
//#endif // Skip Doxygen
};
}
#endif
diff --git a/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp b/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp
index e5b0a156d9..0e2ae7ebf1 100644
--- a/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp
+++ b/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp
@@ -1,523 +1,523 @@
/*============================================================================
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 <mitkAbstractGlobalImageFeature.h>
#include <mitkImageCast.h>
#include <mitkITKImageImport.h>
#include <iterator>
bool mitk::FeatureID::operator < (const FeatureID& rh) const
{
if (this->featureClass < rh.featureClass) return true;
if (this->featureClass == rh.featureClass)
{
if (this->name < rh.name) return true;
if (this->name == rh.name && this->settingID < rh.settingID) return true;
}
return false;
}
bool mitk::FeatureID::operator ==(const FeatureID& rh) const
{
if (this->name != rh.name) return false;
if (this->settingID != rh.settingID) return false;
if (this->featureClass != rh.featureClass) return false;
return true;
}
mitk::FeatureID mitk::CreateFeatureID(FeatureID templateID, std::string name)
{
auto newID = templateID;
newID.name = name;
return newID;
}
static void
ExtractSlicesFromImages(mitk::Image::Pointer image, mitk::Image::Pointer mask,
int direction,
std::vector<mitk::Image::Pointer> &imageVector,
std::vector<mitk::Image::Pointer> &maskVector)
{
typedef itk::Image< double, 2 > FloatImage2DType;
typedef itk::Image< unsigned short, 2 > MaskImage2DType;
typedef itk::Image< double, 3 > FloatImageType;
typedef itk::Image< unsigned short, 3 > MaskImageType;
FloatImageType::Pointer itkFloat = FloatImageType::New();
MaskImageType::Pointer itkMask = MaskImageType::New();
mitk::CastToItkImage(mask, itkMask);
mitk::CastToItkImage(image, itkFloat);
int idxA, idxB, idxC;
switch (direction)
{
case 0:
idxA = 1; idxB = 2; idxC = 0;
break;
case 1:
idxA = 0; idxB = 2; idxC = 1;
break;
case 2:
idxA = 0; idxB = 1; idxC = 2;
break;
default:
idxA = 1; idxB = 2; idxC = 0;
break;
}
auto imageSize = image->GetLargestPossibleRegion().GetSize();
FloatImageType::IndexType index3D;
FloatImage2DType::IndexType index2D;
FloatImage2DType::SpacingType spacing2D;
spacing2D[0] = itkFloat->GetSpacing()[idxA];
spacing2D[1] = itkFloat->GetSpacing()[idxB];
for (unsigned int i = 0; i < imageSize[idxC]; ++i)
{
FloatImage2DType::RegionType region;
FloatImage2DType::IndexType start;
FloatImage2DType::SizeType size;
start[0] = 0; start[1] = 0;
size[0] = imageSize[idxA];
size[1] = imageSize[idxB];
region.SetIndex(start);
region.SetSize(size);
FloatImage2DType::Pointer image2D = FloatImage2DType::New();
image2D->SetRegions(region);
image2D->Allocate();
MaskImage2DType::Pointer mask2D = MaskImage2DType::New();
mask2D->SetRegions(region);
mask2D->Allocate();
unsigned long voxelsInMask = 0;
for (unsigned int a = 0; a < imageSize[idxA]; ++a)
{
for (unsigned int b = 0; b < imageSize[idxB]; ++b)
{
index3D[idxA] = a;
index3D[idxB] = b;
index3D[idxC] = i;
index2D[0] = a;
index2D[1] = b;
image2D->SetPixel(index2D, itkFloat->GetPixel(index3D));
mask2D->SetPixel(index2D, itkMask->GetPixel(index3D));
voxelsInMask += (itkMask->GetPixel(index3D) > 0) ? 1 : 0;
}
}
image2D->SetSpacing(spacing2D);
mask2D->SetSpacing(spacing2D);
mitk::Image::Pointer tmpFloatImage = mitk::Image::New();
tmpFloatImage->InitializeByItk(image2D.GetPointer());
mitk::GrabItkImageMemory(image2D, tmpFloatImage);
mitk::Image::Pointer tmpMaskImage = mitk::Image::New();
tmpMaskImage->InitializeByItk(mask2D.GetPointer());
mitk::GrabItkImageMemory(mask2D, tmpMaskImage);
if (voxelsInMask > 0)
{
imageVector.push_back(tmpFloatImage);
maskVector.push_back(tmpMaskImage);
}
}
}
std::vector<double> mitk::AbstractGlobalImageFeature::SplitDouble(std::string str, char delimiter) {
std::vector<double> internal;
std::stringstream ss(str); // Turn the string into a stream.
std::string tok;
double val;
while (std::getline(ss, tok, delimiter)) {
std::stringstream s2(tok);
s2 >> val;
internal.push_back(val);
}
return internal;
}
void mitk::AbstractGlobalImageFeature::AddQuantifierArguments(mitkCommandLineParser &parser) const
{
std::string name = GetOptionPrefix();
- parser.addArgument(name + "::minimum", name + "::min", mitkCommandLineParser::Float, "Minium Intensity for Quantification", "Defines the minimum Intensity used for Quantification", us::Any());
+ parser.addArgument(name + "::minimum", name + "::min", mitkCommandLineParser::Float, "Minimum Intensity for Quantification", "Defines the minimum Intensity used for Quantification", us::Any());
parser.addArgument(name + "::maximum", name + "::max", mitkCommandLineParser::Float, "Maximum Intensity for Quantification", "Defines the maximum Intensity used for Quantification", us::Any());
parser.addArgument(name + "::bins", name + "::bins", mitkCommandLineParser::Int, "Number of Bins", "Define the number of bins that is used ", us::Any());
parser.addArgument(name + "::binsize", name + "::binsize", mitkCommandLineParser::Float, "Binsize", "Define the size of the used bins", us::Any());
parser.addArgument(name + "::ignore-global-histogram", name + "::ignore-global-histogram", mitkCommandLineParser::Bool, "Ignore the global histogram Parameters", "Ignores the global histogram parameters", us::Any());
parser.addArgument(name + "::ignore-mask-for-histogram", name + "::ignore-mask", mitkCommandLineParser::Bool, "Ignore the global histogram Parameters", "Ignores the global histogram parameters", us::Any());
}
void mitk::AbstractGlobalImageFeature::ConfigureQuantifierSettingsByParameters()
{
unsigned int bins = 0;
double binsize = 0;
double minimum = 0;
double maximum = 0;
auto parsedArgs = GetParameters();
std::string name = GetOptionPrefix();
bool useGlobal = true;
if (parsedArgs.count(name + "::ignore-global-histogram"))
{
useGlobal = false;
SetUseMinimumIntensity(false);
SetUseMaximumIntensity(false);
SetUseBinsize(false);
SetUseBins(false);
}
if (useGlobal)
{
if (parsedArgs.count("ignore-mask-for-histogram"))
{
bool tmp = us::any_cast<bool>(parsedArgs["ignore-mask-for-histogram"]);
SetIgnoreMask(tmp);
}
if (parsedArgs.count("minimum-intensity"))
{
minimum = us::any_cast<float>(parsedArgs["minimum-intensity"]);
SetMinimumIntensity(minimum);
SetUseMinimumIntensity(true);
}
if (parsedArgs.count("maximum-intensity"))
{
maximum = us::any_cast<float>(parsedArgs["maximum-intensity"]);
SetMaximumIntensity(maximum);
SetUseMaximumIntensity(true);
}
if (parsedArgs.count("bins"))
{
bins = us::any_cast<int>(parsedArgs["bins"]);
SetBins(bins);
SetUseBins(true);
}
if (parsedArgs.count("binsize"))
{
binsize = us::any_cast<float>(parsedArgs["binsize"]);
SetBinsize(binsize);
SetUseBinsize(true);
}
}
if (parsedArgs.count(name+"::ignore-mask-for-histogram"))
{
bool tmp = us::any_cast<bool>(parsedArgs[name+"::ignore-mask-for-histogram"]);
SetIgnoreMask(tmp);
}
if (parsedArgs.count(name + "::minimum"))
{
minimum = us::any_cast<float>(parsedArgs[name + "::minimum"]);
SetMinimumIntensity(minimum);
SetUseMinimumIntensity(true);
}
if (parsedArgs.count(name + "::maximum"))
{
maximum = us::any_cast<float>(parsedArgs[name + "::maximum"]);
SetMaximumIntensity(maximum);
SetUseMaximumIntensity(true);
}
if (parsedArgs.count(name + "::bins"))
{
bins = us::any_cast<int>(parsedArgs[name + "::bins"]);
SetBins(bins);
}
if (parsedArgs.count(name + "::binsize"))
{
binsize = us::any_cast<float>(parsedArgs[name + "::binsize"]);
SetBinsize(binsize);
SetUseBinsize(true);
}
}
void mitk::AbstractGlobalImageFeature::ConfigureSettingsByParameters(const ParametersType& /*parameters*/)
{
//Default implementation does nothing.
//Override to change behavior.
}
void mitk::AbstractGlobalImageFeature::InitializeQuantifier(const Image* image, const Image* mask, unsigned int defaultBins)
{
m_Quantifier = IntensityQuantifier::New();
if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBinsize())
m_Quantifier->InitializeByBinsizeAndMaximum(GetMinimumIntensity(), GetMaximumIntensity(), GetBinsize());
else if (GetUseMinimumIntensity() && GetUseBins() && GetUseBinsize())
m_Quantifier->InitializeByBinsizeAndBins(GetMinimumIntensity(), GetBins(), GetBinsize());
else if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBins())
m_Quantifier->InitializeByMinimumMaximum(GetMinimumIntensity(), GetMaximumIntensity(), GetBins());
- // Intialize from Image and Binsize
+ // Initialize from Image and Binsize
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageAndBinsizeAndMinimum(image, GetMinimumIntensity(), GetBinsize());
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageAndBinsizeAndMaximum(image, GetMaximumIntensity(), GetBinsize());
else if (GetUseBinsize() && GetIgnoreMask())
m_Quantifier->InitializeByImageAndBinsize(image, GetBinsize());
// Initialize form Image, Mask and Binsize
else if (GetUseBinsize() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageRegionAndBinsizeAndMinimum(image, mask, GetMinimumIntensity(), GetBinsize());
else if (GetUseBinsize() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageRegionAndBinsizeAndMaximum(image, mask, GetMaximumIntensity(), GetBinsize());
else if (GetUseBinsize())
m_Quantifier->InitializeByImageRegionAndBinsize(image, mask, GetBinsize());
- // Intialize from Image and Bins
+ // Initialize from Image and Bins
else if (GetUseBins() && GetIgnoreMask() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageAndMinimum(image, GetMinimumIntensity(), GetBins());
else if (GetUseBins() && GetIgnoreMask() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageAndMaximum(image, GetMaximumIntensity(), GetBins());
else if (GetUseBins())
m_Quantifier->InitializeByImage(image, GetBins());
- // Intialize from Image, Mask and Bins
+ // Initialize from Image, Mask and Bins
else if (GetUseBins() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageRegionAndMinimum(image, mask, GetMinimumIntensity(), GetBins());
else if (GetUseBins() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageRegionAndMaximum(image, mask, GetMaximumIntensity(), GetBins());
else if (GetUseBins())
m_Quantifier->InitializeByImageRegion(image, mask, GetBins());
// Default
else if (GetIgnoreMask())
m_Quantifier->InitializeByImage(image, GetBins());
else
m_Quantifier->InitializeByImageRegion(image, mask, defaultBins);
}
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureName(const FeatureID& id) const
{
std::string output;
output = m_FeatureClassName + "::";
if (m_EncodeParametersInFeaturePrefix)
{
output += this->GenerateLegacyFeatureEncoding(id) + "::";
}
return output + this->GenerateLegacyFeatureNamePart(id);
};
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureNamePart(const FeatureID& id) const
{
return id.name;
}
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureEncoding(const FeatureID& /*id*/) const
{
return this->QuantifierParameterString();
}
std::string mitk::AbstractGlobalImageFeature::QuantifierParameterString() const
{
std::stringstream ss;
ss.imbue(std::locale("c"));
if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBinsize())
ss << "Min-" << GetMinimumIntensity() << "_Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize();
else if (GetUseMinimumIntensity() && GetUseBins() && GetUseBinsize())
ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins() << "_BS-" << GetBinsize();
else if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBins())
ss << "Min-" << GetMinimumIntensity() << "_Max-" << GetMaximumIntensity() << "_Bins-" << GetBins();
- // Intialize from Image and Binsize
+ // Initialize from Image and Binsize
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_BS-" << GetBinsize() << "_FullImage";
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize() << "_FullImage";
else if (GetUseBinsize() && GetIgnoreMask())
ss << "BS-" << GetBinsize() << "_FullImage";
// Initialize form Image, Mask and Binsize
else if (GetUseBinsize() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_BS-" << GetBinsize();
else if (GetUseBinsize() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize();
else if (GetUseBinsize())
ss << "BS-" << GetBinsize();
- // Intialize from Image and Bins
+ // Initialize from Image and Bins
else if (GetUseBins() && GetIgnoreMask() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins() << "_FullImage";
else if (GetUseBins() && GetIgnoreMask() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_Bins-" << GetBins() << "_FullImage";
else if (GetUseBins())
ss << "Bins-" << GetBins() << "_FullImage";
- // Intialize from Image, Mask and Bins
+ // Initialize from Image, Mask and Bins
else if (GetUseBins() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins();
else if (GetUseBins() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_Bins-" << GetBins();
else if (GetUseBins())
ss << "Bins-" << GetBins();
// Default
else if (GetIgnoreMask())
ss << "Bins-" << GetBins() << "_FullImage";
else
ss << "Bins-" << GetBins();
return ss.str();
}
mitk::FeatureID mitk::AbstractGlobalImageFeature::CreateTemplateFeatureID(std::string settingsSuffix, FeatureID::ParametersType additionalParams)
{
FeatureID newID;
newID.featureClass = this->GetFeatureClassName();
newID.settingID = this->GetShortName();
if (!settingsSuffix.empty())
{
newID.settingID += "_" + settingsSuffix;
}
std::string name = this->GetOptionPrefix();
for (const auto& paramIter : m_Parameters)
{
if (paramIter.first.find(name) == 0)
{
newID.parameters.insert(std::make_pair(paramIter.first, paramIter.second));
}
}
if (m_Quantifier.IsNotNull())
{ //feature class uses the quantifier. So store the information in the feature ID
if (GetUseMinimumIntensity())
{
newID.parameters["minimum-intensity"] = us::Any(GetMinimumIntensity());
}
if (GetUseMaximumIntensity())
{
newID.parameters["minimum-intensity"] = us::Any(GetMaximumIntensity());
}
if (GetUseBinsize())
{
newID.parameters["binsize"] = us::Any(GetBinsize());
}
if (GetBins())
{
newID.parameters["bins"] = us::Any(GetBins());
}
if (GetIgnoreMask())
{
newID.parameters["ignore-mask-for-histogram"] = us::Any(true);
}
}
for (const auto& paramIter : additionalParams)
{
newID.parameters[paramIter.first] = paramIter.second;
}
return newID;
}
void mitk::AbstractGlobalImageFeature::CalculateAndAppendFeaturesSliceWise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID, FeatureListType &featureList, bool checkParameterActivation)
{
auto parsedArgs = GetParameters();
if (!checkParameterActivation || parsedArgs.count(GetLongName()))
{
auto result = CalculateFeaturesSlicewise(image, mask, sliceID);
featureList.insert(featureList.end(), result.begin(), result.end());
}
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::AbstractGlobalImageFeature::CalculateFeaturesSlicewise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID)
{
std::vector<mitk::Image::Pointer> imageVector;
std::vector<mitk::Image::Pointer> maskVector;
ExtractSlicesFromImages(image, mask,sliceID, imageVector, maskVector);
std::vector<mitk::AbstractGlobalImageFeature::FeatureListType> statVector;
for (std::size_t index = 0; index < imageVector.size(); ++index)
{
auto stat = this->CalculateFeatures(imageVector[index], maskVector[index]);
statVector.push_back(stat);
}
if (statVector.size() < 1)
return FeatureListType();
FeatureListType statMean, statStd, result;
for (std::size_t i = 0; i < statVector[0].size(); ++i)
{
auto cElement1 = statVector[0][i];
cElement1.first.name = "SliceWise Mean " + cElement1.first.name;
cElement1.first.legacyName = this->GenerateLegacyFeatureName(cElement1.first);
cElement1.second = 0.0;
auto cElement2 = statVector[0][i];
cElement2.first.name = "SliceWise Var. " + cElement2.first.name;
cElement2.first.legacyName = this->GenerateLegacyFeatureName(cElement2.first);
cElement2.second = 0.0;
statMean.push_back(cElement1);
statStd.push_back(cElement2);
}
for (auto cStat : statVector)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statMean[i].second += cStat[i].second / (1.0*statVector.size());
}
}
for (auto cStat : statVector)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statStd[i].second += (cStat[i].second - statMean[i].second)*(cStat[i].second - statMean[i].second) / (1.0*statVector.size());
}
}
for (auto cStat : statVector)
{
std::copy(cStat.begin(), cStat.end(), std::back_inserter(result));
}
std::copy(statMean.begin(), statMean.end(), std::back_inserter(result));
std::copy(statStd.begin(), statStd.end(), std::back_inserter(result));
return result;
}
void mitk::AbstractGlobalImageFeature::CalculateAndAppendFeatures(const Image* image, const Image* mask, const Image* maskNoNAN, FeatureListType& featureList, bool checkParameterActivation)
{
auto parsedArgs = this->GetParameters();
if (!checkParameterActivation || parsedArgs.count(this->GetLongName()))
{
auto result = this->CalculateFeatures(image, mask, maskNoNAN);
featureList.insert(featureList.end(), result.begin(), result.end());
}
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::AbstractGlobalImageFeature::CalculateFeatures(const Image* image, const Image* mask)
{
auto result = this->DoCalculateFeatures(image, mask);
//ensure legacy names
for (auto& feature : result)
{
feature.first.legacyName = this->GenerateLegacyFeatureName(feature.first);
}
return result;
}
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureNameWOEncoding(const mitk::FeatureID& id)
{
std::string result;
if (id.name.find("SliceWise") == 0)
{
result = id.name.substr(0, 14) + " " + id.featureClass + "::" + id.name.substr(15, -1);
}
else
{
result = id.featureClass + "::" + id.name;
}
return result;
}
diff --git a/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp b/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp
index c7c0bcbadf..9c5fdfd80b 100644
--- a/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp
+++ b/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp
@@ -1,308 +1,308 @@
/*============================================================================
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 <mitkConfigurationHolder.h>
#include <mitkExceptionMacro.h>
#include <sstream>
mitk::ConfigurationHolder::ConfigurationHolder() :
m_ValueType(DT_UNINIZIALIZED)
{
m_GroupValue.clear();
}
void mitk::ConfigurationHolder::SetBool(bool value)
{
m_BoolValue = value;
m_ValueType = DT_BOOL;
}
void mitk::ConfigurationHolder::SetUnsignedInt(unsigned int value)
{
m_UIntValue = value;
m_ValueType = DT_UINT;
}
void mitk::ConfigurationHolder::SetInt(int value)
{
m_IntValue = value;
m_ValueType = DT_INT;
}
void mitk::ConfigurationHolder::SetDouble(double value)
{
m_DoubleValue = value;
m_ValueType = DT_DOUBLE;
}
void mitk::ConfigurationHolder::SetString(std::string value)
{
m_StringValue = value;
m_ValueType = DT_STRING;
}
void mitk::ConfigurationHolder::ClearGroup()
{
m_GroupValue.clear();
m_ValueType = DT_GROUP;
}
void mitk::ConfigurationHolder::AddToGroup(std::string id, const ConfigurationHolder &value)
{
m_GroupValue[id] = value;
m_ValueType = DT_GROUP;
}
bool mitk::ConfigurationHolder::AsBool()
{
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return (m_DoubleValue > 1 || m_DoubleValue < -1);
break;
case DT_STRING:
return (m_StringValue != "0");
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
unsigned int mitk::ConfigurationHolder::AsUnsignedInt()
{
unsigned int result;
std::istringstream ss(m_StringValue);
ss >> result;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return m_DoubleValue;
break;
case DT_STRING:
return result;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
int mitk::ConfigurationHolder::AsInt()
{
int result;
std::istringstream ss(m_StringValue);
ss >> result;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return m_DoubleValue;
break;
case DT_STRING:
return result;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
double mitk::ConfigurationHolder::AsDouble()
{
double result;
std::istringstream ss(m_StringValue);
ss >> result;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return m_DoubleValue > 1;
break;
case DT_STRING:
return result;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
std::string mitk::ConfigurationHolder::AsString()
{
std::ostringstream strs;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
strs << m_BoolValue;
break;
case DT_UINT:
strs << m_UIntValue;
break;
case DT_INT:
strs << m_IntValue;
break;
case DT_DOUBLE:
strs << m_DoubleValue;
break;
case DT_STRING:
return m_StringValue;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
return strs.str();
}
std::vector<std::string> mitk::ConfigurationHolder::AsStringVector()
{
if (m_ValueType != DT_GROUP)
mitkThrow() << "No Group Data, cannot convert to String Vector";
std::vector<std::string> result;
for (auto iter = m_GroupValue.begin(); iter != m_GroupValue.end(); ++iter)
{
result.push_back((*iter).second.AsString());
}
return result;
}
mitk::ConfigurationHolder& mitk::ConfigurationHolder::At(std::string id)
{
return m_GroupValue[id];
}
bool mitk::ConfigurationHolder::AsBool(bool value)
{
try {
return this->AsBool();
}
catch (const mitk::Exception &)
{
return value;
}
}
unsigned int mitk::ConfigurationHolder::AsUnsignedInt(unsigned int value)
{
try {
return this->AsUnsignedInt();
}
catch (const mitk::Exception &)
{
return value;
}
}
int mitk::ConfigurationHolder::AsInt(int value)
{
try {
return this->AsInt();
}
catch (const mitk::Exception &)
{
return value;
}
}
double mitk::ConfigurationHolder::AsDouble(double value)
{
try {
return this->AsDouble();
}
catch (const mitk::Exception &)
{
return value;
}
}
std::string mitk::ConfigurationHolder::AsString(std::string value)
{
try {
return this->AsString();
}
catch (const mitk::Exception &)
{
return value;
}
}
diff --git a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp
index 4ff557a00d..b93f7d1d09 100644
--- a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp
+++ b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp
@@ -1,786 +1,786 @@
/*============================================================================
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 mitkCLPolyToNrrd_cpp
#define mitkCLPolyToNrrd_cpp
#include "time.h"
#include <sstream>
#include <fstream>
#include <mitkIOUtil.h>
#include "mitkCommandLineParser.h"
#include <mitkSplitParameterToVector.h>
#include <mitkGlobalImageFeaturesParameter.h>
#include <mitkGIFCooccurenceMatrix.h>
#include <mitkGIFCooccurenceMatrix2.h>
#include <mitkGIFGreyLevelRunLength.h>
#include <mitkGIFFirstOrderStatistics.h>
#include <mitkGIFFirstOrderHistogramStatistics.h>
#include <mitkGIFFirstOrderNumericStatistics.h>
#include <mitkGIFVolumetricStatistics.h>
#include <mitkGIFVolumetricDensityStatistics.h>
#include <mitkGIFGreyLevelSizeZone.h>
#include <mitkGIFGreyLevelDistanceZone.h>
#include <mitkGIFImageDescriptionFeatures.h>
#include <mitkGIFLocalIntensity.h>
#include <mitkGIFCurvatureStatistic.h>
#include <mitkGIFIntensityVolumeHistogramFeatures.h>
#include <mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h>
#include <mitkGIFNeighbouringGreyLevelDependenceFeatures.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkITKImageImport.h>
#include <mitkConvert2Dto3DImageFilter.h>
#include <mitkCLResultWriter.h>
#include <mitkCLResultXMLWriter.h>
#include <mitkVersion.h>
#include <iostream>
#include <locale>
#include <itkImageDuplicator.h>
#include <itkImageRegionIterator.h>
#include "itkNearestNeighborInterpolateImageFunction.h"
#include "itkResampleImageFilter.h"
#include <QApplication>
#include <mitkStandaloneDataStorage.h>
#include "QmitkRegisterClasses.h"
#include "QmitkRenderWindow.h"
#include "vtkRenderLargeImage.h"
#include "vtkPNGWriter.h"
typedef itk::Image< double, 3 > FloatImageType;
typedef itk::Image< unsigned short, 3 > MaskImageType;
template <class charT>
class punct_facet : public std::numpunct<charT> {
public:
punct_facet(charT sep) :
m_Sep(sep)
{
}
protected:
charT do_decimal_point() const override { return m_Sep; }
private:
charT m_Sep;
};
template<typename TPixel, unsigned int VImageDimension>
void
ResampleImage(itk::Image<TPixel, VImageDimension>* itkImage, float resolution, mitk::Image::Pointer& newImage)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::ResampleImageFilter<ImageType, ImageType> ResampleFilterType;
typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
auto spacing = itkImage->GetSpacing();
auto size = itkImage->GetLargestPossibleRegion().GetSize();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
size[i] = size[i] / (1.0*resolution)*(1.0*spacing[i])+1.0;
}
spacing.Fill(resolution);
resampler->SetInput(itkImage);
resampler->SetSize(size);
resampler->SetOutputSpacing(spacing);
resampler->SetOutputOrigin(itkImage->GetOrigin());
resampler->SetOutputDirection(itkImage->GetDirection());
resampler->Update();
newImage->InitializeByItk(resampler->GetOutput());
mitk::GrabItkImageMemory(resampler->GetOutput(), newImage);
}
template<typename TPixel, unsigned int VImageDimension>
static void
CreateNoNaNMask(itk::Image<TPixel, VImageDimension>* itkValue, mitk::Image::Pointer mask, mitk::Image::Pointer& newMask)
{
typedef itk::Image< TPixel, VImageDimension> LFloatImageType;
typedef itk::Image< unsigned short, VImageDimension> LMaskImageType;
typename LMaskImageType::Pointer itkMask = LMaskImageType::New();
mitk::CastToItkImage(mask, itkMask);
typedef itk::ImageDuplicator< LMaskImageType > DuplicatorType;
typename DuplicatorType::Pointer duplicator = DuplicatorType::New();
duplicator->SetInputImage(itkMask);
duplicator->Update();
auto tmpMask = duplicator->GetOutput();
itk::ImageRegionIterator<LMaskImageType> mask1Iter(itkMask, itkMask->GetLargestPossibleRegion());
itk::ImageRegionIterator<LMaskImageType> mask2Iter(tmpMask, tmpMask->GetLargestPossibleRegion());
itk::ImageRegionIterator<LFloatImageType> imageIter(itkValue, itkValue->GetLargestPossibleRegion());
while (!mask1Iter.IsAtEnd())
{
mask2Iter.Set(0);
if (mask1Iter.Value() > 0)
{
// Is not NaN
if (imageIter.Value() == imageIter.Value())
{
mask2Iter.Set(1);
}
}
++mask1Iter;
++mask2Iter;
++imageIter;
}
newMask->InitializeByItk(tmpMask);
mitk::GrabItkImageMemory(tmpMask, newMask);
}
template<typename TPixel, unsigned int VImageDimension>
static void
ResampleMask(itk::Image<TPixel, VImageDimension>* itkMoving, mitk::Image::Pointer ref, mitk::Image::Pointer& newMask)
{
typedef itk::Image< TPixel, VImageDimension> LMaskImageType;
typedef itk::NearestNeighborInterpolateImageFunction< LMaskImageType> NearestNeighborInterpolateImageFunctionType;
typedef itk::ResampleImageFilter<LMaskImageType, LMaskImageType> ResampleFilterType;
typename NearestNeighborInterpolateImageFunctionType::Pointer nn_interpolator = NearestNeighborInterpolateImageFunctionType::New();
typename LMaskImageType::Pointer itkRef = LMaskImageType::New();
mitk::CastToItkImage(ref, itkRef);
typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
resampler->SetInput(itkMoving);
resampler->SetReferenceImage(itkRef);
resampler->UseReferenceImageOn();
resampler->SetInterpolator(nn_interpolator);
resampler->Update();
newMask->InitializeByItk(resampler->GetOutput());
mitk::GrabItkImageMemory(resampler->GetOutput(), newMask);
}
static void
ExtractSlicesFromImages(mitk::Image::Pointer image, mitk::Image::Pointer mask,
mitk::Image::Pointer maskNoNaN, mitk::Image::Pointer morphMask,
int direction,
std::vector<mitk::Image::Pointer> &imageVector,
std::vector<mitk::Image::Pointer> &maskVector,
std::vector<mitk::Image::Pointer> &maskNoNaNVector,
std::vector<mitk::Image::Pointer> &morphMaskVector)
{
typedef itk::Image< double, 2 > FloatImage2DType;
typedef itk::Image< unsigned short, 2 > MaskImage2DType;
FloatImageType::Pointer itkFloat = FloatImageType::New();
MaskImageType::Pointer itkMask = MaskImageType::New();
MaskImageType::Pointer itkMaskNoNaN = MaskImageType::New();
MaskImageType::Pointer itkMorphMask = MaskImageType::New();
mitk::CastToItkImage(mask, itkMask);
mitk::CastToItkImage(maskNoNaN, itkMaskNoNaN);
mitk::CastToItkImage(image, itkFloat);
mitk::CastToItkImage(morphMask, itkMorphMask);
int idxA, idxB, idxC;
switch (direction)
{
case 0:
idxA = 1; idxB = 2; idxC = 0;
break;
case 1:
idxA = 0; idxB = 2; idxC = 1;
break;
case 2:
idxA = 0; idxB = 1; idxC = 2;
break;
default:
idxA = 1; idxB = 2; idxC = 0;
break;
}
auto imageSize = image->GetLargestPossibleRegion().GetSize();
FloatImageType::IndexType index3D;
FloatImage2DType::IndexType index2D;
FloatImage2DType::SpacingType spacing2D;
spacing2D[0] = itkFloat->GetSpacing()[idxA];
spacing2D[1] = itkFloat->GetSpacing()[idxB];
for (unsigned int i = 0; i < imageSize[idxC]; ++i)
{
FloatImage2DType::RegionType region;
FloatImage2DType::IndexType start;
FloatImage2DType::SizeType size;
start[0] = 0; start[1] = 0;
size[0] = imageSize[idxA];
size[1] = imageSize[idxB];
region.SetIndex(start);
region.SetSize(size);
FloatImage2DType::Pointer image2D = FloatImage2DType::New();
image2D->SetRegions(region);
image2D->Allocate();
MaskImage2DType::Pointer mask2D = MaskImage2DType::New();
mask2D->SetRegions(region);
mask2D->Allocate();
MaskImage2DType::Pointer masnNoNaN2D = MaskImage2DType::New();
masnNoNaN2D->SetRegions(region);
masnNoNaN2D->Allocate();
MaskImage2DType::Pointer morph2D = MaskImage2DType::New();
morph2D->SetRegions(region);
morph2D->Allocate();
unsigned long voxelsInMask = 0;
for (unsigned int a = 0; a < imageSize[idxA]; ++a)
{
for (unsigned int b = 0; b < imageSize[idxB]; ++b)
{
index3D[idxA] = a;
index3D[idxB] = b;
index3D[idxC] = i;
index2D[0] = a;
index2D[1] = b;
image2D->SetPixel(index2D, itkFloat->GetPixel(index3D));
mask2D->SetPixel(index2D, itkMask->GetPixel(index3D));
masnNoNaN2D->SetPixel(index2D, itkMaskNoNaN->GetPixel(index3D));
morph2D->SetPixel(index2D, itkMorphMask->GetPixel(index3D));
voxelsInMask += (itkMask->GetPixel(index3D) > 0) ? 1 : 0;
}
}
image2D->SetSpacing(spacing2D);
mask2D->SetSpacing(spacing2D);
masnNoNaN2D->SetSpacing(spacing2D);
morph2D->SetSpacing(spacing2D);
mitk::Image::Pointer tmpFloatImage = mitk::Image::New();
tmpFloatImage->InitializeByItk(image2D.GetPointer());
mitk::GrabItkImageMemory(image2D, tmpFloatImage);
mitk::Image::Pointer tmpMaskImage = mitk::Image::New();
tmpMaskImage->InitializeByItk(mask2D.GetPointer());
mitk::GrabItkImageMemory(mask2D, tmpMaskImage);
mitk::Image::Pointer tmpMaskNoNaNImage = mitk::Image::New();
tmpMaskNoNaNImage->InitializeByItk(masnNoNaN2D.GetPointer());
mitk::GrabItkImageMemory(masnNoNaN2D, tmpMaskNoNaNImage);
mitk::Image::Pointer tmpMorphMaskImage = mitk::Image::New();
tmpMorphMaskImage->InitializeByItk(morph2D.GetPointer());
mitk::GrabItkImageMemory(morph2D, tmpMorphMaskImage);
if (voxelsInMask > 0)
{
imageVector.push_back(tmpFloatImage);
maskVector.push_back(tmpMaskImage);
maskNoNaNVector.push_back(tmpMaskNoNaNImage);
morphMaskVector.push_back(tmpMorphMaskImage);
}
}
}
static
void SaveSliceOrImageAsPNG(mitk::Image::Pointer image, mitk::Image::Pointer mask, std::string path, int index)
{
// Create a Standalone Datastorage for the single purpose of saving screenshots..
mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New();
QmitkRenderWindow renderWindow;
renderWindow.GetRenderer()->SetDataStorage(ds);
auto nodeI = mitk::DataNode::New();
nodeI->SetData(image);
auto nodeM = mitk::DataNode::New();
nodeM->SetData(mask);
ds->Add(nodeI);
ds->Add(nodeM);
auto geo = ds->ComputeBoundingGeometry3D(ds->GetAll());
mitk::RenderingManager::GetInstance()->InitializeViews(geo);
mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController();
unsigned int numberOfSteps = 1;
if (sliceNaviController)
{
numberOfSteps = sliceNaviController->GetStepper()->GetSteps();
sliceNaviController->GetStepper()->SetPos(0);
}
renderWindow.show();
renderWindow.resize(256, 256);
for (unsigned int currentStep = 0; currentStep < numberOfSteps; ++currentStep)
{
if (sliceNaviController)
{
sliceNaviController->GetStepper()->SetPos(currentStep);
}
renderWindow.GetRenderer()->PrepareRender();
vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow();
mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2);
auto vtkRender = baserenderer->GetVtkRenderer();
vtkRender->GetRenderWindow()->WaitForCompletion();
vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New();
magnifier->SetInput(vtkRender);
magnifier->SetMagnification(3.0);
std::stringstream ss;
ss << path << "_Idx-" << index << "_Step-"<<currentStep<<".png";
std::string tmpImageName;
ss >> tmpImageName;
auto fileWriter = vtkPNGWriter::New();
fileWriter->SetInputConnection(magnifier->GetOutputPort());
fileWriter->SetFileName(tmpImageName.c_str());
fileWriter->Write();
fileWriter->Delete();
}
}
int main(int argc, char* argv[])
{
// Commented : Updated to a common interface, include, if possible, mask is type unsigned short, uses Quantification, Comments
// Name follows standard scheme with Class Name::Feature Name
// Commented 2: Updated to use automatic inclusion of list of parameters if required.
mitk::GIFImageDescriptionFeatures::Pointer ipCalculator = mitk::GIFImageDescriptionFeatures::New(); // Commented 2, Tested
mitk::GIFFirstOrderStatistics::Pointer firstOrderCalculator = mitk::GIFFirstOrderStatistics::New(); //Commented 2
mitk::GIFFirstOrderHistogramStatistics::Pointer firstOrderHistoCalculator = mitk::GIFFirstOrderHistogramStatistics::New(); // Commented 2, Tested
mitk::GIFFirstOrderNumericStatistics::Pointer firstOrderNumericCalculator = mitk::GIFFirstOrderNumericStatistics::New(); // Commented 2, Tested
mitk::GIFVolumetricStatistics::Pointer volCalculator = mitk::GIFVolumetricStatistics::New(); // Commented 2, Tested
mitk::GIFVolumetricDensityStatistics::Pointer voldenCalculator = mitk::GIFVolumetricDensityStatistics::New(); // Commented 2, Tested
mitk::GIFCooccurenceMatrix::Pointer coocCalculator = mitk::GIFCooccurenceMatrix::New(); // Commented 2, Will not be tested
mitk::GIFCooccurenceMatrix2::Pointer cooc2Calculator = mitk::GIFCooccurenceMatrix2::New(); //Commented 2
mitk::GIFNeighbouringGreyLevelDependenceFeature::Pointer ngldCalculator = mitk::GIFNeighbouringGreyLevelDependenceFeature::New(); //Commented 2, Tested
mitk::GIFGreyLevelRunLength::Pointer rlCalculator = mitk::GIFGreyLevelRunLength::New(); // Commented 2
mitk::GIFGreyLevelSizeZone::Pointer glszCalculator = mitk::GIFGreyLevelSizeZone::New(); // Commented 2, Tested
mitk::GIFGreyLevelDistanceZone::Pointer gldzCalculator = mitk::GIFGreyLevelDistanceZone::New(); //Commented 2, Tested
mitk::GIFLocalIntensity::Pointer lociCalculator = mitk::GIFLocalIntensity::New(); //Commented 2, Tested
mitk::GIFIntensityVolumeHistogramFeatures::Pointer ivohCalculator = mitk::GIFIntensityVolumeHistogramFeatures::New(); // Commented 2
mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::Pointer ngtdCalculator = mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::New(); //Commented 2, Tested
mitk::GIFCurvatureStatistic::Pointer curvCalculator = mitk::GIFCurvatureStatistic::New(); //Commented 2, Tested
std::vector<mitk::AbstractGlobalImageFeature::Pointer> features;
features.push_back(volCalculator.GetPointer());
features.push_back(voldenCalculator.GetPointer());
features.push_back(curvCalculator.GetPointer());
features.push_back(firstOrderCalculator.GetPointer());
features.push_back(firstOrderNumericCalculator.GetPointer());
features.push_back(firstOrderHistoCalculator.GetPointer());
features.push_back(ivohCalculator.GetPointer());
features.push_back(lociCalculator.GetPointer());
features.push_back(coocCalculator.GetPointer());
features.push_back(cooc2Calculator.GetPointer());
features.push_back(ngldCalculator.GetPointer());
features.push_back(rlCalculator.GetPointer());
features.push_back(glszCalculator.GetPointer());
features.push_back(gldzCalculator.GetPointer());
features.push_back(ipCalculator.GetPointer());
features.push_back(ngtdCalculator.GetPointer());
mitkCommandLineParser parser;
parser.setArgumentPrefix("--", "-");
mitk::cl::GlobalImageFeaturesParameter param;
param.AddParameter(parser);
parser.addArgument("--","-", mitkCommandLineParser::String, "---", "---", us::Any(),true);
for (auto cFeature : features)
{
cFeature->AddArguments(parser);
}
parser.addArgument("--", "-", mitkCommandLineParser::String, "---", "---", us::Any(), true);
parser.addArgument("description","d",mitkCommandLineParser::String,"Text","Description that is added to the output",us::Any());
parser.addArgument("direction", "dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... Without dimension 0,1,2... ", us::Any());
parser.addArgument("slice-wise", "slice", mitkCommandLineParser::String, "Int", "Allows to specify if the image is processed slice-wise (number giving direction) ", us::Any());
parser.addArgument("output-mode", "omode", mitkCommandLineParser::Int, "Int", "Defines the format of the output. 0: (Default) results of an image / slice are written in a single row;"
" 1: results of an image / slice are written in a single column; 2: store the result of on image as structured radiomocs report (XML).");
// Miniapp Infos
parser.setCategory("Classification Tools");
parser.setTitle("Global Image Feature calculator");
parser.setDescription("Calculates different global statistics for a given segmentation / image combination");
parser.setContributor("German Cancer Research Center (DKFZ)");
std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
param.ParseParameter(parsedArgs);
if (parsedArgs.size()==0)
{
return EXIT_FAILURE;
}
if ( parsedArgs.count("help") || parsedArgs.count("h"))
{
return EXIT_SUCCESS;
}
std::string version = "Version: 1.23";
MITK_INFO << version;
std::ofstream log;
if (param.useLogfile)
{
log.open(param.logfilePath, std::ios::app);
log << std::endl;
log << version;
log << "Image: " << param.imagePath;
log << "Mask: " << param.maskPath;
}
if (param.useDecimalPoint)
{
std::cout.imbue(std::locale(std::cout.getloc(), new punct_facet<char>(param.decimalPoint)));
}
//representing the original loaded image data without any prepropcessing that might come.
mitk::Image::Pointer loadedImage = mitk::IOUtil::Load<mitk::Image>(param.imagePath);
//representing the original loaded mask data without any prepropcessing that might come.
mitk::Image::Pointer loadedMask = mitk::IOUtil::Load<mitk::Image>(param.maskPath);
mitk::Image::Pointer image = loadedImage;
mitk::Image::Pointer mask = loadedMask;
mitk::Image::Pointer tmpImage = loadedImage;
mitk::Image::Pointer tmpMask = loadedMask;
mitk::Image::Pointer morphMask = mask;
if (param.useMorphMask)
{
morphMask = mitk::IOUtil::Load<mitk::Image>(param.morphPath);
}
log << " Check for Dimensions -";
if ((image->GetDimension() != mask->GetDimension()))
{
MITK_INFO << "Dimension of image does not match. ";
MITK_INFO << "Correct one image, may affect the result";
if (image->GetDimension() == 2)
{
mitk::Convert2Dto3DImageFilter::Pointer multiFilter2 = mitk::Convert2Dto3DImageFilter::New();
multiFilter2->SetInput(tmpImage);
multiFilter2->Update();
image = multiFilter2->GetOutput();
}
if (mask->GetDimension() == 2)
{
mitk::Convert2Dto3DImageFilter::Pointer multiFilter3 = mitk::Convert2Dto3DImageFilter::New();
multiFilter3->SetInput(tmpMask);
multiFilter3->Update();
mask = multiFilter3->GetOutput();
}
}
int writeDirection = 0;
if (parsedArgs.count("output-mode"))
{
writeDirection = us::any_cast<int>(parsedArgs["output-mode"]);
}
log << " Check for Resolution -";
if (param.resampleToFixIsotropic)
{
mitk::Image::Pointer newImage = mitk::Image::New();
AccessByItk_2(image, ResampleImage, param.resampleResolution, newImage);
image = newImage;
}
log << " Resample if required -";
if (param.resampleMask)
{
mitk::Image::Pointer newMaskImage = mitk::Image::New();
AccessByItk_2(mask, ResampleMask, image, newMaskImage);
mask = newMaskImage;
}
if ( ! mitk::Equal(mask->GetGeometry(0)->GetOrigin(), image->GetGeometry(0)->GetOrigin()))
{
MITK_INFO << "Not equal Origins";
if (param.ensureSameSpace)
{
MITK_INFO << "Warning!";
MITK_INFO << "The origin of the input image and the mask do not match. They are";
MITK_INFO << "now corrected. Please check to make sure that the images still match";
image->GetGeometry(0)->SetOrigin(mask->GetGeometry(0)->GetOrigin());
} else
{
return -1;
}
}
log << " Check for Equality -";
if ( ! mitk::Equal(mask->GetGeometry(0)->GetSpacing(), image->GetGeometry(0)->GetSpacing()))
{
MITK_INFO << "Not equal Spacing";
if (param.ensureSameSpace)
{
MITK_INFO << "Warning!";
MITK_INFO << "The spacing of the mask was set to match the spacing of the input image.";
MITK_INFO << "This might cause unintended spacing of the mask image";
image->GetGeometry(0)->SetSpacing(mask->GetGeometry(0)->GetSpacing());
} else
{
MITK_INFO << "The spacing of the mask and the input images is not equal.";
- MITK_INFO << "Terminating the programm. You may use the '-fi' option";
+ MITK_INFO << "Terminating the program. You may use the '-fi' option";
return -1;
}
}
int direction = 0;
if (parsedArgs.count("direction"))
{
direction = mitk::cl::splitDouble(parsedArgs["direction"].ToString(), ';')[0];
}
MITK_INFO << "Start creating Mask without NaN";
mitk::Image::Pointer maskNoNaN = mitk::Image::New();
AccessByItk_2(image, CreateNoNaNMask, mask, maskNoNaN);
//CreateNoNaNMask(mask, image, maskNoNaN);
bool sliceWise = false;
int sliceDirection = 0;
unsigned int currentSlice = 0;
bool imageToProcess = true;
std::vector<mitk::Image::Pointer> floatVector;
std::vector<mitk::Image::Pointer> maskVector;
std::vector<mitk::Image::Pointer> maskNoNaNVector;
std::vector<mitk::Image::Pointer> morphMaskVector;
if ((parsedArgs.count("slice-wise")) && image->GetDimension() > 2)
{
MITK_INFO << "Enabled slice-wise";
sliceWise = true;
sliceDirection = mitk::cl::splitDouble(parsedArgs["slice-wise"].ToString(), ';')[0];
MITK_INFO << sliceDirection;
ExtractSlicesFromImages(image, mask, maskNoNaN, morphMask, sliceDirection, floatVector, maskVector, maskNoNaNVector, morphMaskVector);
MITK_INFO << "Slice";
}
log << " Configure features -";
for (auto cFeature : features)
{
if (param.defineGlobalMinimumIntensity)
{
cFeature->SetMinimumIntensity(param.globalMinimumIntensity);
cFeature->SetUseMinimumIntensity(true);
}
if (param.defineGlobalMaximumIntensity)
{
cFeature->SetMaximumIntensity(param.globalMaximumIntensity);
cFeature->SetUseMaximumIntensity(true);
}
if (param.defineGlobalNumberOfBins)
{
cFeature->SetBins(param.globalNumberOfBins);
MITK_INFO << param.globalNumberOfBins;
}
cFeature->SetParameters(parsedArgs);
cFeature->SetDirection(direction);
cFeature->SetEncodeParametersInFeaturePrefix(param.encodeParameter);
}
bool addDescription = parsedArgs.count("description");
mitk::cl::FeatureResultWriter writer(param.outputPath, writeDirection);
if (param.useDecimalPoint)
{
writer.SetDecimalPoint(param.decimalPoint);
}
std::string description = "";
if (addDescription)
{
description = parsedArgs["description"].ToString();
}
mitk::Image::Pointer cImage = image;
mitk::Image::Pointer cMask = mask;
mitk::Image::Pointer cMaskNoNaN = maskNoNaN;
mitk::Image::Pointer cMorphMask = morphMask;
if (param.useHeader)
{
writer.AddColumn("SoftwareVersion");
writer.AddColumn("Patient");
writer.AddColumn("Image");
writer.AddColumn("Segmentation");
}
// Create a QTApplication and a Datastorage
// This is necessary in order to save screenshots of
// each image / slice.
QApplication qtapplication(argc, argv);
QmitkRegisterClasses();
std::vector<mitk::AbstractGlobalImageFeature::FeatureListType> allStats;
log << " Begin Processing -";
while (imageToProcess)
{
if (sliceWise)
{
cImage = floatVector[currentSlice];
cMask = maskVector[currentSlice];
cMaskNoNaN = maskNoNaNVector[currentSlice];
cMorphMask = morphMaskVector[currentSlice];
imageToProcess = (floatVector.size()-1 > (currentSlice)) ? true : false ;
}
else
{
imageToProcess = false;
}
if (param.writePNGScreenshots)
{
SaveSliceOrImageAsPNG(cImage, cMask, param.pngScreenshotsPath, currentSlice);
}
if (param.writeAnalysisImage)
{
mitk::IOUtil::Save(cImage, param.anaylsisImagePath);
}
if (param.writeAnalysisMask)
{
mitk::IOUtil::Save(cMask, param.analysisMaskPath);
}
mitk::AbstractGlobalImageFeature::FeatureListType stats;
for (auto cFeature : features)
{
log << " Calculating " << cFeature->GetFeatureClassName() << " -";
cFeature->SetMorphMask(cMorphMask);
cFeature->CalculateAndAppendFeatures(cImage, cMask, cMaskNoNaN, stats, !param.calculateAllFeatures);
}
for (std::size_t i = 0; i < stats.size(); ++i)
{
std::cout << stats[i].first.legacyName << " - " << stats[i].second << std::endl;
}
writer.AddHeader(description, currentSlice, stats, param.useHeader, addDescription);
if (true)
{
writer.AddSubjectInformation(MITK_REVISION);
writer.AddSubjectInformation(param.imageFolder);
writer.AddSubjectInformation(param.imageName);
writer.AddSubjectInformation(param.maskName);
}
writer.AddResult(description, currentSlice, stats, param.useHeader, addDescription);
allStats.push_back(stats);
++currentSlice;
}
log << " Process Slicewise -";
if (sliceWise)
{
mitk::AbstractGlobalImageFeature::FeatureListType statMean, statStd;
for (std::size_t i = 0; i < allStats[0].size(); ++i)
{
auto cElement1 = allStats[0][i];
cElement1.first.legacyName = "SliceWise Mean " + cElement1.first.legacyName;
cElement1.second = 0.0;
auto cElement2 = allStats[0][i];
cElement2.first.legacyName = "SliceWise Var. " + cElement2.first.legacyName;
cElement2.second = 0.0;
statMean.push_back(cElement1);
statStd.push_back(cElement2);
}
for (auto cStat : allStats)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statMean[i].second += cStat[i].second / (1.0*allStats.size());
}
}
for (auto cStat : allStats)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statStd[i].second += (cStat[i].second - statMean[i].second)*(cStat[i].second - statMean[i].second) / (1.0*allStats.size());
}
}
for (std::size_t i = 0; i < statMean.size(); ++i)
{
std::cout << statMean[i].first.legacyName << " - " << statMean[i].second << std::endl;
std::cout << statStd[i].first.legacyName << " - " << statStd[i].second << std::endl;
}
if (true)
{
writer.AddSubjectInformation(MITK_REVISION);
writer.AddSubjectInformation(param.imageFolder);
writer.AddSubjectInformation(param.imageName);
writer.AddSubjectInformation(param.maskName + " - Mean");
}
writer.AddResult(description, currentSlice, statMean, param.useHeader, addDescription);
if (true)
{
writer.AddSubjectInformation(MITK_REVISION);
writer.AddSubjectInformation(param.imageFolder);
writer.AddSubjectInformation(param.imageName);
writer.AddSubjectInformation(param.maskName + " - Var.");
}
writer.AddResult(description, currentSlice, statStd, param.useHeader, addDescription);
}
int returnCode = EXIT_SUCCESS;
if (!param.outputXMLPath.empty())
{
if (sliceWise)
{
MITK_ERROR << "Xml output is not supported in slicewise mode";
returnCode = EXIT_FAILURE;
}
else
{
mitk::cl::CLResultXMLWriter xmlWriter;
xmlWriter.SetCLIArgs(parsedArgs);
xmlWriter.SetFeatures(allStats.front());
xmlWriter.SetImage(loadedImage);
xmlWriter.SetMask(loadedMask);
xmlWriter.SetMethodName("CLGlobalImageFeatures");
xmlWriter.SetMethodVersion(version + "(mitk: " MITK_VERSION_STRING+")");
xmlWriter.SetOrganisation("German Cancer Research Center (DKFZ)");
xmlWriter.SetPipelineUID(param.pipelineUID);
xmlWriter.write(param.outputXMLPath);
}
}
if (param.useLogfile)
{
log << "Finished calculation" << std::endl;
log.close();
}
return returnCode;
}
#endif
diff --git a/Modules/Classification/CLMiniApps/CLN4.cpp b/Modules/Classification/CLMiniApps/CLN4.cpp
index ba778fed55..a7aa9d061a 100644
--- a/Modules/Classification/CLMiniApps/CLN4.cpp
+++ b/Modules/Classification/CLMiniApps/CLN4.cpp
@@ -1,117 +1,117 @@
/*============================================================================
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 "mitkCommandLineParser.h"
#include "mitkIOUtil.h"
#include <mitkImageCast.h>
#include "mitkCommandLineParser.h"
#include <itkN4BiasFieldCorrectionImageFilter.h>
#include <itkSTAPLEImageFilter.h>
int main(int argc, char* argv[])
{
typedef itk::Image<unsigned char, 3> MaskImageType;
typedef itk::Image<float, 3> ImageType;
typedef itk::N4BiasFieldCorrectionImageFilter < ImageType, MaskImageType, ImageType > FilterType;
mitkCommandLineParser parser;
parser.setTitle("N4 Bias Field Correction");
parser.setCategory("Classification Command Tools");
parser.setDescription("");
parser.setContributor("German Cancer Research Center (DKFZ)");
parser.setArgumentPrefix("--", "-");
// Add command line argument names
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.addArgument("input", "i", mitkCommandLineParser::Directory, "Input file:", "Input file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("mask", "m", mitkCommandLineParser::File, "Output file:", "Mask file", us::Any(), false, false, false, mitkCommandLineParser::Output);
parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file:", "Output file", us::Any(), false, false, false, mitkCommandLineParser::Output);
parser.addArgument("number-of-controllpoints", "noc", mitkCommandLineParser::Int, "Parameter", "The noc for the point grid size defining the B-spline estimate (default 4)", us::Any(), true);
parser.addArgument("number-of-fitting-levels", "nofl", mitkCommandLineParser::Int, "Parameter", "Number of fitting levels for the multi-scale approach (default 1)", us::Any(), true);
parser.addArgument("number-of-histogram-bins", "nofl", mitkCommandLineParser::Int, "Parameter", "number of bins defining the log input intensity histogram (default 200)", us::Any(), true);
parser.addArgument("spline-order", "so", mitkCommandLineParser::Int, "Parameter", "Define the spline order (default 3)", us::Any(), true);
parser.addArgument("winer-filter-noise", "wfn", mitkCommandLineParser::Float, "Parameter", "Noise estimate defining the Wiener filter (default 0.01)", us::Any(), true);
parser.addArgument("number-of-maximum-iterations", "nomi", mitkCommandLineParser::Int, "Parameter", "Spezifies the maximum number of iterations per run", us::Any(), true);
// ToDo: Number Of Maximum Iterations durchschleifen
std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
MaskImageType::Pointer itkMsk = MaskImageType::New();
mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(parsedArgs["mask"].ToString());
mitk::CastToItkImage(img, itkMsk);
ImageType::Pointer itkImage = ImageType::New();
mitk::Image::Pointer img2 = mitk::IOUtil::Load<mitk::Image>(parsedArgs["input"].ToString());
mitk::CastToItkImage(img2, itkImage);
FilterType::Pointer filter = FilterType::New();
filter->SetInput(itkImage);
filter->SetMaskImage(itkMsk);
if (parsedArgs.count("number-of-controllpoints") > 0)
{
int variable = us::any_cast<int>(parsedArgs["maximum-iterations"]);
- MITK_INFO << "Number of controll points: " << variable;
+ MITK_INFO << "Number of control points: " << variable;
filter->SetNumberOfControlPoints(variable);
}
if (parsedArgs.count("number-of-fitting-levels") > 0)
{
int variable = us::any_cast<int>(parsedArgs["number-of-fitting-levels"]);
MITK_INFO << "Number of fitting levels: " << variable;
filter->SetNumberOfFittingLevels(variable);
}
if (parsedArgs.count("number-of-histogram-bins") > 0)
{
int variable = us::any_cast<int>(parsedArgs["number-of-histogram-bins"]);
MITK_INFO << "Number of histogram bins: " << variable;
filter->SetNumberOfHistogramBins(variable);
}
if (parsedArgs.count("spline-order") > 0)
{
int variable = us::any_cast<int>(parsedArgs["spline-order"]);
MITK_INFO << "Spline Order " << variable;
filter->SetSplineOrder(variable);
}
if (parsedArgs.count("winer-filter-noise") > 0)
{
float variable = us::any_cast<float>(parsedArgs["winer-filter-noise"]);
MITK_INFO << "Number of histogram bins: " << variable;
filter->SetWienerFilterNoise(variable);
}
if (parsedArgs.count("number-of-maximum-iterations") > 0)
{
int variable = us::any_cast<int>(parsedArgs["number-of-maximum-iterations"]);
MITK_INFO << "Number of Maximum Iterations: " << variable;
auto list = filter->GetMaximumNumberOfIterations();
list.Fill(variable);
filter->SetMaximumNumberOfIterations(list);
}
filter->Update();
auto out = filter->GetOutput();
mitk::Image::Pointer outImg = mitk::Image::New();
mitk::CastToMitkImage(out, outImg);
mitk::IOUtil::Save(outImg, parsedArgs["output"].ToString());
return EXIT_SUCCESS;
}
diff --git a/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h b/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h
index ab99b4415f..f3de451fac 100644
--- a/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h
+++ b/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h
@@ -1,267 +1,267 @@
/*============================================================================
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 itkCooccurenceMatrixFeatureFunctor_h
#define itkCooccurenceMatrixFeatureFunctor_h
#include "itkConstNeighborhoodIterator.h"
#include <itkHistogramToTextureFeaturesFilter.h>
#include <itkHistogram.h>
#include <bitset>
/*
* To Do:
* - Enable normalization of GLCM
*/
namespace itk
{
namespace Functor
{
-/** \brief Functor for texture feature calculation based on the Cooccurence matrix
+/** \brief Functor for texture feature calculation based on the Cooccurrence matrix
*/
template< typename TNeighborhoodType, typename TPixelOutputType>
class NeighborhoodCooccurenceMatrix
{
public:
// Functor defines
typedef itk::Statistics::Histogram< double, itk::Statistics::DenseFrequencyContainer2 > HistogramType;
typedef typename TNeighborhoodType::OffsetType OffsetType;
typedef typename HistogramType::SizeType SizeType;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
typedef typename itk::Statistics::HistogramToTextureFeaturesFilter<HistogramType> HistoToFeatureFilter;
static const unsigned int OutputCount = 8;
typedef vnl_vector_fixed<TPixelOutputType, OutputCount> OutputVectorType;
typedef TNeighborhoodType NeighborhoodType;
enum OutputFeatures
{
ENERGY, ENTROPY, CORRELATION, INERTIA, CLUSTERSHADE, CLUSTERPROMINENCE, HARALICKCORRELATION, INVERSEDIFFERENCEMOMENT
};
static const char * GetFeatureName(unsigned int f )
{
static const char * const FeatureNames[] = {"ENERGY", "ENTROPY", "CORRELATION", "INERTIA", "CLUSTERSHADE", "CLUSTERPROMINENCE", "HARALICKCORRELATION", "INVERSEDIFFERENCEMOMENT"};
return FeatureNames[f];
}
enum OffsetDirection
{
DIR_x1_x0_x0 = 1,
DIR_x1_x1_x0 = 2,
DIR_x0_x1_x0 = 4,
DIR_n1_x1_x0 = 8,
DIR_x1_x0_x1 = 16,
DIR_x1_x1_x1 = 32,
DIR_x0_x1_x1 = 64,
DIR_n1_x1_x1 = 128,
DIR_x1_x0_n1 = 256,
DIR_x1_x1_n1 = 512,
DIR_x0_x1_n1 = 1024,
DIR_n1_x1_n1 = 2048,
DIR_x0_x0_x1 = 4096,
ROTATION_INVARIANT = 8191
};
static std::map<OffsetDirection, OffsetType > CreateStdOffsets()
{
std::map<OffsetDirection,OffsetType> offsetMap;
{
itk::Offset<3> off = {{1,0,0}};
offsetMap[DIR_x1_x0_x0] = off;
}
{
itk::Offset<3> off = {{1,1,0}};
offsetMap[DIR_x1_x1_x0] = off;
}
{
itk::Offset<3> off = {{0,1,0}};
offsetMap[DIR_x0_x1_x0] = off;
}
{
itk::Offset<3> off = {{-1,1,0}};
offsetMap[DIR_n1_x1_x0] = off;
}
{
itk::Offset<3> off = {{1,0,1}};
offsetMap[DIR_x1_x0_x1]= off;
}
{
itk::Offset<3> off = {{1,1,1}};
offsetMap[DIR_x1_x1_x1] = off;
}
{
itk::Offset<3> off = {{0,1,1}};
offsetMap[DIR_x0_x1_x1] = off;
}
{
itk::Offset<3> off = {{-1,1,1}};
offsetMap[DIR_n1_x1_x1] = off;
}
{
itk::Offset<3> off = {{1,0,-1}};
offsetMap[DIR_x1_x0_n1] = off;
}
{
itk::Offset<3> off = {{1,1,-1}};
offsetMap[DIR_x1_x1_n1] = off;
}
{
itk::Offset<3> off = {{0,1,-1}};
offsetMap[DIR_x0_x1_n1] = off;
}
{
itk::Offset<3> off = {{-1,1,-1}};
offsetMap[DIR_n1_x1_n1] = off;
}
{
itk::Offset<3> off = {{0,0,1}};
offsetMap[DIR_x0_x0_x1] = off;
}
return offsetMap;
}
int m_DirectionFlags;
std::map<OffsetDirection, OffsetType> m_offsetMap;
unsigned int m_levels;
void DirectionFlags(int flags)
{
DirectionFlags(static_cast<OffsetDirection>(flags));
}
void DirectionFlags(OffsetDirection flags)
{
m_DirectionFlags = flags;
}
void SetLevels(unsigned int lvl)
{
m_levels = lvl;
}
NeighborhoodCooccurenceMatrix()
{
m_offsetMap = CreateStdOffsets();
m_levels = 5;
m_DirectionFlags =
DIR_x1_x0_x0; /*| DIR_x1_x1_x0 | DIR_x0_x1_x0 |
DIR_n1_x1_x0 | DIR_x1_x0_x1 | DIR_x1_x1_x1 |
DIR_x0_x1_x1 | DIR_n1_x1_x1 | DIR_x1_x0_n1 |
DIR_x1_x1_n1 | DIR_x0_x1_n1 | DIR_n1_x1_n1 |
DIR_x0_x0_x1;*/
std::cout << "NeighborhoodCooccurenceMatrix" << std::endl;
}
inline OutputVectorType operator()(const TNeighborhoodType & it) const
{
double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::min();
for (unsigned int i = 0; i < it.Size(); ++i)
{
double value = it.GetPixel(i);
max = (value > max) ? value : max;
min = (value < min) ? value : min;
}
if ( min >= max)
{
OutputVectorType nullRes; nullRes.fill(0);
return nullRes;
}
SizeType size;
MeasurementVectorType minBorder;
MeasurementVectorType maxBorder;
OffsetType g1, g2;
- size.SetSize(2); // 2D Historgram
+ size.SetSize(2); // 2D Histogram
size.Fill(m_levels); // 5 bins each dim
minBorder.SetSize(2); // min range value
minBorder.Fill(min);
maxBorder.SetSize(2); // max range value
maxBorder.Fill(max);
MeasurementVectorType cooccur;
cooccur.SetSize(2);
OutputVectorType output_vector;
output_vector.fill(0);
double div_num_dirs = 0;
//std::bitset<14> x(m_DirectionFlags);
//std::cout << "Direction flag " << x;
for(typename std::map<OffsetDirection,OffsetType>::const_iterator dir_it = m_offsetMap.begin(),
end = m_offsetMap.end(); dir_it != end; dir_it ++){
if(! (dir_it->first & m_DirectionFlags))
continue;
div_num_dirs++;
HistogramType::Pointer histogram = HistogramType::New();
histogram->SetMeasurementVectorSize(2);
histogram->Initialize(size, minBorder, maxBorder);
for (unsigned int i = 0; i < it.Size(); ++i)
{
// grayvalue pair (g1,g2)
// g1 ~ g2 with ~ as relation (e.g. a offset calculation)
g1 = it.GetOffset(i);
g2 = g1 + dir_it->second;
cooccur[0] = it.GetPixel(i);
cooccur[1] = it.GetImagePointer()->GetPixel(it.GetIndex(i)+dir_it->second);
histogram->IncreaseFrequencyOfMeasurement(cooccur, 1);
std::swap(cooccur[0],cooccur[1]);
histogram->IncreaseFrequencyOfMeasurement(cooccur, 1);
}
//To do Normalize GLCM N_g Number of levels
HistoToFeatureFilter::Pointer filter = HistoToFeatureFilter::New();
filter->SetInput(histogram);
filter->Update();
output_vector[ENERGY] += filter->GetEnergy();
output_vector[ENTROPY] += filter->GetEntropy();
output_vector[CORRELATION] += filter->GetCorrelation();
output_vector[INERTIA] += filter->GetInertia();
output_vector[CLUSTERSHADE] += filter->GetClusterShade();
output_vector[CLUSTERPROMINENCE] += filter->GetClusterProminence();
output_vector[HARALICKCORRELATION] += filter->GetHaralickCorrelation();
output_vector[INVERSEDIFFERENCEMOMENT] += filter->GetInverseDifferenceMoment();
}
//std::cout << "Number of directions " << div_num_dirs << std::endl;
// output_vector /= div_num_dirs;
return output_vector;
}
};
}// end namespace functor
} // end namespace itk
#endif // itkNeighborhoodFunctors_h
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h
index f7992dd190..3d4180b029 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h
@@ -1,273 +1,273 @@
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkEnhancedHistogramToTextureFeaturesFilter_h
#define __itkEnhancedHistogramToTextureFeaturesFilter_h
#include "itkHistogram.h"
#include "itkMacro.h"
#include "itkProcessObject.h"
#include "itkSimpleDataObjectDecorator.h"
/** Get built-in type. Creates member Get"name"() (e.g., GetVisibility()); */
#define itkMacroGLCMFeatureGetter(name) \
const MeasurementObjectType * Get##name##Output() const; \
\
MeasurementType Get##name() const;
namespace itk
{
namespace Statistics
{
/** \class EnhancedHistogramToTextureFeaturesFilter
* \brief This class computes texture feature coefficients from a grey level
* co-occurrence matrix.
*
* This class computes features that summarize image texture, given a grey level
* co-occurrence matrix (generated by a ScalarImageToCooccurrenceMatrixFilter
* or related class).
*
* The features calculated are as follows (where \f$ g(i, j) \f$ is the element in
* cell i, j of a a normalized GLCM):
*
* "Energy" \f$ = f_1 = \sum_{i,j}g(i, j)^2 \f$
*
* "Entropy" \f$ = f_2 = -\sum_{i,j}g(i, j) \log_2 g(i, j)\f$, or 0 if \f$g(i, j) = 0\f$
*
* "Correlation" \f$ = f_3 = \sum_{i,j}\frac{(i - \mu)(j - \mu)g(i, j)}{\sigma^2} \f$
*
* "Difference Moment" \f$= f_4 = \sum_{i,j}\frac{1}{1 + (i - j)^2}g(i, j) \f$
*
* "Inertia" \f$ = f_5 = \sum_{i,j}(i - j)^2g(i, j) \f$ (sometimes called "contrast.")
*
* "Cluster Shade" \f$ = f_6 = \sum_{i,j}((i - \mu) + (j - \mu))^3 g(i, j) \f$
*
* "Cluster Prominence" \f$ = f_7 = \sum_{i,j}((i - \mu) + (j - \mu))^4 g(i, j) \f$
*
* "Haralick's Correlation" \f$ = f_8 = \frac{\sum_{i,j}(i, j) g(i, j) -\mu_t^2}{\sigma_t^2} \f$
* where \f$\mu_t\f$ and \f$\sigma_t\f$ are the mean and standard deviation of the row
* (or column, due to symmetry) sums.
*
* Above, \f$ \mu = \f$ (weighted pixel average) \f$ = \sum_{i,j}i \cdot g(i, j) =
* \sum_{i,j}j \cdot g(i, j) \f$ (due to matrix summetry), and
*
* \f$ \sigma = \f$ (weighted pixel variance) \f$ = \sum_{i,j}(i - \mu)^2 \cdot g(i, j) =
* \sum_{i,j}(j - \mu)^2 \cdot g(i, j) \f$ (due to matrix summetry)
*
* A good texture feature set to use is the Conners, Trivedi and Harlow set:
* features 1, 2, 4, 5, 6, and 7. There is some correlation between the various
* features, so using all of them at the same time is not necessarialy a good idea.
*
* NOTA BENE: The input histogram will be forcably normalized!
* This algorithm takes three passes through the input
* histogram if the histogram was already normalized, and four if not.
*
* Print references:
*
* Haralick, R.M., K. Shanmugam and I. Dinstein. 1973. Textural Features for
* Image Classification. IEEE Transactions on Systems, Man and Cybernetics.
* SMC-3(6):610-620.
*
* Haralick, R.M. 1979. Statistical and Structural Approaches to Texture.
* Proceedings of the IEEE, 67:786-804.
*
- * R.W. Conners and C.A. Harlow. A Theoretical Comaprison of Texture Algorithms.
+ * R.W. Conners and C.A. Harlow. A Theoretical Comparison of Texture Algorithms.
* IEEE Transactions on Pattern Analysis and Machine Intelligence, 2:204-222, 1980.
*
* R.W. Conners, M.M. Trivedi, and C.A. Harlow. Segmentation of a High-Resolution
* Urban Scene using Texture Operators. Computer Vision, Graphics and Image
* Processing, 25:273-310, 1984.
*
* \sa ScalarImageToCooccurrenceMatrixFilter
* \sa ScalarImageToTextureFeaturesFilter
*
* Author: Zachary Pincus
* \ingroup ITKStatistics
*/
template< typename THistogram >
class EnhancedHistogramToTextureFeaturesFilter:public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedHistogramToTextureFeaturesFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro(EnhancedHistogramToTextureFeaturesFilter, ProcessObject);
/** standard New() method support */
itkNewMacro(Self);
typedef THistogram HistogramType;
typedef typename HistogramType::Pointer HistogramPointer;
typedef typename HistogramType::ConstPointer HistogramConstPointer;
typedef typename HistogramType::MeasurementType MeasurementType;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
typedef typename HistogramType::IndexType IndexType;
typedef typename HistogramType::AbsoluteFrequencyType AbsoluteFrequencyType;
typedef typename HistogramType::RelativeFrequencyType RelativeFrequencyType;
typedef typename HistogramType::TotalAbsoluteFrequencyType
TotalAbsoluteFrequencyType;
typedef typename HistogramType::TotalRelativeFrequencyType
TotalRelativeFrequencyType;
/** Container to hold relative frequencies of the histogram */
typedef std::vector< RelativeFrequencyType > RelativeFrequencyContainerType;
/** Method to Set/Get the input Histogram */
using Superclass::SetInput;
void SetInput(const HistogramType *histogram);
const HistogramType * GetInput() const;
/** Smart Pointer type to a DataObject. */
typedef DataObject::Pointer DataObjectPointer;
/** Type of DataObjects used for scalar outputs */
typedef SimpleDataObjectDecorator< MeasurementType > MeasurementObjectType;
/** Return energy texture value. */
MeasurementType GetEnergy() const;
const MeasurementObjectType * GetEnergyOutput() const;
/** Return entropy texture value. */
MeasurementType GetEntropy() const;
const MeasurementObjectType * GetEntropyOutput() const;
/** return correlation texture value. */
MeasurementType GetCorrelation() const;
const MeasurementObjectType * GetCorrelationOutput() const;
/** Return inverse difference moment texture value. */
MeasurementType GetInverseDifferenceMoment() const;
const MeasurementObjectType * GetInverseDifferenceMomentOutput() const;
/** Return inertia texture value. */
MeasurementType GetInertia() const;
const MeasurementObjectType * GetInertiaOutput() const;
/** Return cluster shade texture value. */
MeasurementType GetClusterShade() const;
const MeasurementObjectType * GetClusterShadeOutput() const;
/** Return cluster prominence texture value. */
MeasurementType GetClusterProminence() const;
const MeasurementObjectType * GetClusterProminenceOutput() const;
/** Return Haralick correlation texture value. */
MeasurementType GetHaralickCorrelation() const;
const MeasurementObjectType * GetHaralickCorrelationOutput() const;
itkMacroGLCMFeatureGetter(Autocorrelation);
itkMacroGLCMFeatureGetter(Contrast);
itkMacroGLCMFeatureGetter(Dissimilarity);
itkMacroGLCMFeatureGetter(MaximumProbability);
itkMacroGLCMFeatureGetter(InverseVariance);
itkMacroGLCMFeatureGetter(Homogeneity1);
itkMacroGLCMFeatureGetter(ClusterTendency);
itkMacroGLCMFeatureGetter(Variance);
itkMacroGLCMFeatureGetter(SumAverage);
itkMacroGLCMFeatureGetter(SumEntropy);
itkMacroGLCMFeatureGetter(SumVariance);
itkMacroGLCMFeatureGetter(DifferenceAverage);
itkMacroGLCMFeatureGetter(DifferenceEntropy);
itkMacroGLCMFeatureGetter(DifferenceVariance);
itkMacroGLCMFeatureGetter(InverseDifferenceMomentNormalized);
itkMacroGLCMFeatureGetter(InverseDifferenceNormalized);
itkMacroGLCMFeatureGetter(InverseDifference);
itkMacroGLCMFeatureGetter(JointAverage);
itkMacroGLCMFeatureGetter(FirstMeasureOfInformationCorrelation);
itkMacroGLCMFeatureGetter(SecondMeasureOfInformationCorrelation);
/** Texture feature types */
typedef enum {
Energy,
Entropy,
Correlation,
InverseDifferenceMoment,
Inertia,
ClusterShade,
ClusterProminence,
HaralickCorrelation,
Autocorrelation,
Contrast,
Dissimilarity,
MaximumProbability,
InverseVariance,
Homogeneity1,
ClusterTendency,
Variance,
SumAverage,
SumEntropy,
SumVariance,
DifferenceAverage,
DifferenceEntropy,
DifferenceVariance,
InverseDifferenceMomentNormalized,
InverseDifferenceNormalized,
InverseDifference,
JointAverage,
FirstMeasureOfInformationCorrelation,
SecondMeasureOfInformationCorrelation,
InvalidFeatureName
} TextureFeatureName;
/** convenience method to access the texture values */
MeasurementType GetFeature(TextureFeatureName name);
protected:
EnhancedHistogramToTextureFeaturesFilter();
~EnhancedHistogramToTextureFeaturesFilter() override {}
void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;
/** Make a DataObject to be used for output output. */
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE;
void GenerateData() ITK_OVERRIDE;
private:
EnhancedHistogramToTextureFeaturesFilter(const Self &); //purposely not implemented
void operator=(const Self &); //purposely not implemented
void ComputeMeansAndVariances(double & pixelMean, double & marginalMean,
double & marginalDevSquared, double & pixelVariance);
RelativeFrequencyContainerType m_RelativeFrequencyContainer;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedHistogramToTextureFeaturesFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h
index 1f58958dd6..78bec89ce3 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h
@@ -1,295 +1,295 @@
/*============================================================================
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.
============================================================================*/
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_h
#define __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_h
#include "itkImage.h"
#include "itkHistogram.h"
#include "itkNumericTraits.h"
#include "itkVectorContainer.h"
namespace itk
{
namespace Statistics
{
/** \class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter
* \brief This class computes a run length matrix (histogram) from
* a given image and a mask image if provided. Run length matrces are
* used for image texture description.
*
* This filters creates a grey-level run length matrix from a N-D scalar
* image. This is another possible texture description. See the following
* references.
* M. M. Galloway. Texture analysis using gray level run lengths. Computer
* Graphics and Image Processing, 4:172-179, 1975.
*
* A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of
* run lengths for texture analysis. Pattern Recognition Letters, 11:415-420,
* 1990.
*
* B. R. Dasarathy and E. B. Holder. Image characterizations based on joint
* gray-level run-length distributions. Pattern Recognition Letters, 12:490-502,
* 1991.
*
* The basic idea is as follows:
* Given an image and an offset (e.g. (1, -1) for a 2-d image), each element
* in the joint histogram describes the frequency for a particular distance/
* intensity pair within a given image. This distance/intensity pair can be
* described as follows: we start at a given voxel which has some intensity.
* We then "jump" to neighboring pixels in increments provided by the offset(s)
* as long as the pixel to which we are jumping is within the same intensity
* bin as the original voxel. The distance component is given by the distance
* from the original to the final voxel satisfying our jumping criteria.
*
* The offset (or offsets) along which the co-occurences are calculated can be
* set by the user. Traditionally, only one offset is used per histogram, and
* offset components in the range [-1, 1] are used. For rotation-invariant
* features averages of features computed over several histograms with different
* offsets are generally used, instead of computing features from one histogram
* create with several offsets. Additionally, instead of using offsets of two or
* more pixels in any direction, multi-resolution techniques (e.g. image
* pyramids) are generally used to deal with texture at different spatial
* resolutions.
*
* This class calculates a 2-d histogram of all the intensity/distance pairs in
* the given image's requested region, for a given set of offsets. That is, if
* a given offset falls outside of the requested region (or outside the mask)
* at a particular point, that distance/intensity pair will not be added to
* the matrix.
*
* The number of histogram bins on each axis can be set (defaults to 256). Also,
* by default the histogram min and max corresponds to the largest and smallest
* possible pixel value of that pixel type. To customize the histogram bounds
* for a given image, the max and min pixel values that will be placed in the
* histogram can be set manually. NB: The min and max are INCLUSIVE.
*
* Further, the type of histogram frequency container used is an optional
* template parameter. By default, a dense container is used, but for images
* with little texture or in cases where the user wants more histogram bins,
* a sparse container can be used for the histogram instead.
*
* WARNING: This probably won't work for pixels of double or long-double type
* unless you set the histogram min and max manually. This is because the largest
* histogram bin by default has max value of the largest possible pixel value
* plus 1. For double and long-double types, whose "RealType" as defined by the
* NumericTraits class is the same, and thus cannot hold any larger values,
* this would cause a float overflow.
*
* IJ article: https://hdl.handle.net/1926/1374
*
* \sa ScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter
* \sa EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter
* \sa HistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter
*
* \author: Nick Tustison
* \ingroup ITKStatistics
*/
template<typename TImageType, typename THistogramFrequencyContainer =
DenseFrequencyContainer2>
class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter : public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro( EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter, ProcessObject );
/** standard New() method support */
itkNewMacro( Self );
typedef TImageType ImageType;
typedef typename ImageType::Pointer ImagePointer;
typedef typename ImageType::ConstPointer ImageConstPointer;
typedef typename ImageType::PixelType PixelType;
typedef typename ImageType::IndexType IndexType;
typedef typename ImageType::RegionType RegionType;
typedef typename ImageType::SizeType RadiusType;
typedef typename ImageType::OffsetType OffsetType;
typedef VectorContainer<unsigned char, OffsetType> OffsetVector;
typedef typename OffsetVector::Pointer OffsetVectorPointer;
typedef typename ImageType::PointType PointType;
typedef typename NumericTraits<PixelType>::RealType MeasurementType;
typedef typename NumericTraits<PixelType>::RealType RealType;
typedef Histogram<MeasurementType, THistogramFrequencyContainer>
HistogramType;
typedef typename HistogramType::Pointer HistogramPointer;
typedef typename HistogramType::ConstPointer HistogramConstPointer;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
/** ImageDimension constants */
itkStaticConstMacro( ImageDimension, unsigned int,
TImageType::ImageDimension );
/** Specify the default number of bins per axis */
itkStaticConstMacro( DefaultBinsPerAxis, unsigned int, 256 );
/**
* Set the offsets over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offsets.
* Note: for each individual offset in the OffsetVector, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
itkSetObjectMacro( Offsets, OffsetVector );
/**
* Set offset over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offset(s).
* Note: for each individual offset, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
void SetOffset( const OffsetType offset );
void AddOffsets( const std::vector<OffsetType> offset );
/**
* Get the current offset(s).
*/
itkGetModifiableObjectMacro(Offsets, OffsetVector );
/** Set number of histogram bins along each axis */
itkSetMacro( NumberOfBinsPerAxis, unsigned int );
/** Get number of histogram bins along each axis */
itkGetConstMacro( NumberOfBinsPerAxis, unsigned int );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetPixelValueMinMax( PixelType min, PixelType max );
/** Get the min pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Min, PixelType );
/** Get the max pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Max, PixelType );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetDistanceValueMinMax( RealType min, RealType max );
/**
* Get the min distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MinDistance, RealType );
/**
* Get the max distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MaxDistance, RealType );
/** Method to set the input image */
using Superclass::SetInput;
void SetInput( const ImageType *image );
/** Method to get the input image */
const ImageType * GetInput() const;
/** Method to set the mask image */
void SetMaskImage( const ImageType *image );
/** Method to get the mask image */
const ImageType * GetMaskImage() const;
/** method to get the Histogram */
const HistogramType * GetOutput() const;
/** method to get the Histogram */
double* GetSiMatrix() const;
/**
* Set the pixel value of the mask that should be considered "inside" the
* object. Defaults to 1.
*/
itkSetMacro( InsidePixelValue, PixelType );
itkGetConstMacro( InsidePixelValue, PixelType );
protected:
EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter();
~EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter() override {};
void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE;
/** Standard itk::ProcessObject subclass method. */
typedef DataObject::Pointer DataObjectPointer;
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput( DataObjectPointerArraySizeType idx ) ITK_OVERRIDE;
/** This method causes the filter to generate its output. */
void GenerateData() ITK_OVERRIDE;
/**
* Normalize the direction of the offset before it is applied.
- * The last non-zero dimension of the offest has to be positive in order
+ * The last non-zero dimension of the offset has to be positive in order
* to match to scanning order of the iterator. Only the sign is changed.
* For example, the input offset (-1, 0) will be normalized as
* (1, 0).
* */
void NormalizeOffsetDirection(OffsetType &offset);
private:
unsigned int m_NumberOfBinsPerAxis;
PixelType m_Min;
PixelType m_Max;
RealType m_MinDistance;
RealType m_MaxDistance;
PixelType m_InsidePixelValue;
MeasurementVectorType m_LowerBound;
MeasurementVectorType m_UpperBound;
OffsetVectorPointer m_Offsets;
double * m_siMatrix;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h
index dfdaf31361..b5a7463414 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h
@@ -1,288 +1,288 @@
/*============================================================================
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.
============================================================================*/
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkEnhancedScalarImageToRunLengthMatrixFilter_h
#define __itkEnhancedScalarImageToRunLengthMatrixFilter_h
#include "itkImage.h"
#include "itkHistogram.h"
#include "itkNumericTraits.h"
#include "itkVectorContainer.h"
namespace itk
{
namespace Statistics
{
/** \class EnhancedScalarImageToRunLengthMatrixFilter
* \brief This class computes a run length matrix (histogram) from
* a given image and a mask image if provided. Run length matrces are
* used for image texture description.
*
* This filters creates a grey-level run length matrix from a N-D scalar
* image. This is another possible texture description. See the following
* references.
* M. M. Galloway. Texture analysis using gray level run lengths. Computer
* Graphics and Image Processing, 4:172-179, 1975.
*
* A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of
* run lengths for texture analysis. Pattern Recognition Letters, 11:415-420,
* 1990.
*
* B. R. Dasarathy and E. B. Holder. Image characterizations based on joint
* gray-level run-length distributions. Pattern Recognition Letters, 12:490-502,
* 1991.
*
* The basic idea is as follows:
* Given an image and an offset (e.g. (1, -1) for a 2-d image), each element
* in the joint histogram describes the frequency for a particular distance/
* intensity pair within a given image. This distance/intensity pair can be
* described as follows: we start at a given voxel which has some intensity.
* We then "jump" to neighboring pixels in increments provided by the offset(s)
* as long as the pixel to which we are jumping is within the same intensity
* bin as the original voxel. The distance component is given by the distance
* from the original to the final voxel satisfying our jumping criteria.
*
* The offset (or offsets) along which the co-occurences are calculated can be
* set by the user. Traditionally, only one offset is used per histogram, and
* offset components in the range [-1, 1] are used. For rotation-invariant
* features averages of features computed over several histograms with different
* offsets are generally used, instead of computing features from one histogram
* create with several offsets. Additionally, instead of using offsets of two or
* more pixels in any direction, multi-resolution techniques (e.g. image
* pyramids) are generally used to deal with texture at different spatial
* resolutions.
*
* This class calculates a 2-d histogram of all the intensity/distance pairs in
* the given image's requested region, for a given set of offsets. That is, if
* a given offset falls outside of the requested region (or outside the mask)
* at a particular point, that distance/intensity pair will not be added to
* the matrix.
*
* The number of histogram bins on each axis can be set (defaults to 256). Also,
* by default the histogram min and max corresponds to the largest and smallest
* possible pixel value of that pixel type. To customize the histogram bounds
* for a given image, the max and min pixel values that will be placed in the
* histogram can be set manually. NB: The min and max are INCLUSIVE.
*
* Further, the type of histogram frequency container used is an optional
* template parameter. By default, a dense container is used, but for images
* with little texture or in cases where the user wants more histogram bins,
* a sparse container can be used for the histogram instead.
*
* WARNING: This probably won't work for pixels of double or long-double type
* unless you set the histogram min and max manually. This is because the largest
* histogram bin by default has max value of the largest possible pixel value
* plus 1. For double and long-double types, whose "RealType" as defined by the
* NumericTraits class is the same, and thus cannot hold any larger values,
* this would cause a float overflow.
*
* IJ article: https://hdl.handle.net/1926/1374
*
* \sa ScalarImageToRunLengthFeaturesFilter
* \sa EnhancedScalarImageToRunLengthMatrixFilter
* \sa HistogramToRunLengthFeaturesFilter
*
* \author: Nick Tustison
* \ingroup ITKStatistics
*/
template<typename TImageType, typename THistogramFrequencyContainer =
DenseFrequencyContainer2>
class EnhancedScalarImageToRunLengthMatrixFilter : public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedScalarImageToRunLengthMatrixFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro( EnhancedScalarImageToRunLengthMatrixFilter, ProcessObject );
/** standard New() method support */
itkNewMacro( Self );
typedef TImageType ImageType;
typedef typename ImageType::Pointer ImagePointer;
typedef typename ImageType::ConstPointer ImageConstPointer;
typedef typename ImageType::PixelType PixelType;
typedef typename ImageType::IndexType IndexType;
typedef typename ImageType::RegionType RegionType;
typedef typename ImageType::SizeType RadiusType;
typedef typename ImageType::OffsetType OffsetType;
typedef VectorContainer<unsigned char, OffsetType> OffsetVector;
typedef typename OffsetVector::Pointer OffsetVectorPointer;
typedef typename ImageType::PointType PointType;
typedef typename NumericTraits<PixelType>::RealType MeasurementType;
typedef typename NumericTraits<PixelType>::RealType RealType;
typedef Histogram<MeasurementType, THistogramFrequencyContainer>
HistogramType;
typedef typename HistogramType::Pointer HistogramPointer;
typedef typename HistogramType::ConstPointer HistogramConstPointer;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
/** ImageDimension constants */
itkStaticConstMacro( ImageDimension, unsigned int,
TImageType::ImageDimension );
/** Specify the default number of bins per axis */
itkStaticConstMacro( DefaultBinsPerAxis, unsigned int, 256 );
/**
* Set the offsets over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offsets.
* Note: for each individual offset in the OffsetVector, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
itkSetObjectMacro( Offsets, OffsetVector );
/**
* Set offset over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offset(s).
* Note: for each individual offset, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
void SetOffset( const OffsetType offset );
/**
* Get the current offset(s).
*/
itkGetModifiableObjectMacro(Offsets, OffsetVector );
/** Set number of histogram bins along each axis */
itkSetMacro( NumberOfBinsPerAxis, unsigned int );
/** Get number of histogram bins along each axis */
itkGetConstMacro( NumberOfBinsPerAxis, unsigned int );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetPixelValueMinMax( PixelType min, PixelType max );
/** Get the min pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Min, PixelType );
/** Get the max pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Max, PixelType );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetDistanceValueMinMax( RealType min, RealType max );
/**
* Get the min distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MinDistance, RealType );
/**
* Get the max distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MaxDistance, RealType );
/** Method to set the input image */
using Superclass::SetInput;
void SetInput( const ImageType *image );
/** Method to get the input image */
const ImageType * GetInput() const;
/** Method to set the mask image */
void SetMaskImage( const ImageType *image );
/** Method to get the mask image */
const ImageType * GetMaskImage() const;
/** method to get the Histogram */
const HistogramType * GetOutput() const;
/**
* Set the pixel value of the mask that should be considered "inside" the
* object. Defaults to 1.
*/
itkSetMacro( InsidePixelValue, PixelType );
itkGetConstMacro( InsidePixelValue, PixelType );
protected:
EnhancedScalarImageToRunLengthMatrixFilter();
~EnhancedScalarImageToRunLengthMatrixFilter() override {};
void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE;
/** Standard itk::ProcessObject subclass method. */
typedef DataObject::Pointer DataObjectPointer;
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput( DataObjectPointerArraySizeType idx ) ITK_OVERRIDE;
/** This method causes the filter to generate its output. */
void GenerateData() ITK_OVERRIDE;
/**
* Normalize the direction of the offset before it is applied.
- * The last non-zero dimension of the offest has to be positive in order
+ * The last non-zero dimension of the offset has to be positive in order
* to match to scanning order of the iterator. Only the sign is changed.
* For example, the input offset (-1, 0) will be normalized as
* (1, 0).
* */
void NormalizeOffsetDirection(OffsetType &offset);
private:
unsigned int m_NumberOfBinsPerAxis;
PixelType m_Min;
PixelType m_Max;
RealType m_MinDistance;
RealType m_MaxDistance;
PixelType m_InsidePixelValue;
MeasurementVectorType m_LowerBound;
MeasurementVectorType m_UpperBound;
OffsetVectorPointer m_Offsets;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedScalarImageToRunLengthMatrixFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx
index 88389fd5d9..9a1d292ce5 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx
@@ -1,405 +1,405 @@
/*============================================================================
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.
============================================================================*/
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkEnhancedScalarImageToSizeZoneMatrixFilter_hxx
#define __itkEnhancedScalarImageToSizeZoneMatrixFilter_hxx
#include "itkEnhancedScalarImageToSizeZoneMatrixFilter.h"
#include "itkConstNeighborhoodIterator.h"
#include "itkNeighborhood.h"
#include "vnl/vnl_math.h"
#include "itkMacro.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkMaskImageFilter.h"
#include "itkLabelStatisticsImageFilter.h"
#include "itkScalarConnectedComponentImageFilter.h"
#include "itkRelabelComponentImageFilter.h"
#include "itkCastImageFilter.h"
#include <mitkLog.h>
namespace itk
{
namespace Statistics
{
template<typename TImageType, typename THistogramFrequencyContainer>
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::EnhancedScalarImageToSizeZoneMatrixFilter() :
m_NumberOfBinsPerAxis( itkGetStaticConstMacro( DefaultBinsPerAxis ) ),
m_Min( NumericTraits<PixelType>::NonpositiveMin() ),
m_Max( NumericTraits<PixelType>::max() ),
m_MinDistance( NumericTraits<RealType>::ZeroValue() ),
m_MaxDistance( NumericTraits<RealType>::max() ),
m_InsidePixelValue( NumericTraits<PixelType>::OneValue() )
{
this->SetNumberOfRequiredInputs( 1 );
this->SetNumberOfRequiredOutputs( 1 );
const unsigned int measurementVectorSize = 2;
this->ProcessObject::SetNthOutput( 0, this->MakeOutput( 0 ) );
HistogramType *output = const_cast<HistogramType *>( this->GetOutput() );
output->SetMeasurementVectorSize( measurementVectorSize );
this->m_LowerBound.SetSize( measurementVectorSize );
this->m_UpperBound.SetSize( measurementVectorSize );
this->m_LowerBound[0] = this->m_Min;
this->m_LowerBound[1] = this->m_MinDistance;
this->m_UpperBound[0] = this->m_Max;
this->m_UpperBound[1] = this->m_MaxDistance;
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetOffset( const OffsetType offset )
{
OffsetVectorPointer offsetVector = OffsetVector::New();
offsetVector->push_back( offset );
this->SetOffsets( offsetVector );
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetInput( const ImageType *image )
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput( 0, const_cast<ImageType *>( image ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetMaskImage( const ImageType *image )
{
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput( 1, const_cast<ImageType *>( image ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
const TImageType *
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetInput() const
{
if( this->GetNumberOfInputs() < 1 )
{
return ITK_NULLPTR;
}
return static_cast<const ImageType *>( this->ProcessObject::GetInput( 0 ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
const TImageType *
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetMaskImage() const
{
if( this->GetNumberOfInputs() < 2 )
{
return ITK_NULLPTR;
}
return static_cast<const ImageType *>( this->ProcessObject::GetInput( 1 ) );
}
template<typename TImageType, typename THistogramFrequencyContainer>
const typename EnhancedScalarImageToSizeZoneMatrixFilter<TImageType,
THistogramFrequencyContainer >::HistogramType *
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetOutput() const
{
const HistogramType *output =
static_cast<const HistogramType *>( this->ProcessObject::GetOutput( 0 ) );
return output;
}
template<typename TImageType, typename THistogramFrequencyContainer>
typename EnhancedScalarImageToSizeZoneMatrixFilter<TImageType,
THistogramFrequencyContainer>::DataObjectPointer
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) )
{
return HistogramType::New().GetPointer();
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::GenerateData()
{
HistogramType *output =
static_cast<HistogramType *>( this->ProcessObject::GetOutput( 0 ) );
const ImageType * inputImage = this->GetInput();
const ImageType * maskImage = this->GetMaskImage();
// First, create an appropriate histogram with the right number of bins
// and mins and maxes correct for the image type.
typename HistogramType::SizeType size( output->GetMeasurementVectorSize() );
size.Fill( this->m_NumberOfBinsPerAxis );
this->m_LowerBound[0] = this->m_Min;
this->m_LowerBound[1] = this->m_MinDistance;
this->m_UpperBound[0] = this->m_Max;
this->m_UpperBound[1] = this->m_MaxDistance;
output->Initialize( size, this->m_LowerBound, this->m_UpperBound );
MeasurementVectorType run( output->GetMeasurementVectorSize() );
typename HistogramType::IndexType hIndex;
//Cast the image to a float image - with no respect to the incoming image
//to prevent some non-templated itk issues
typedef itk::Image<float, 3> FloatImageType;
typedef itk::CastImageFilter<ImageType, FloatImageType> CastFilterType;
typename CastFilterType::Pointer caster = CastFilterType::New();
caster->SetInput(inputImage);
caster->Update();
typename FloatImageType::Pointer floatImage = caster->GetOutput();
//MITK_WARN << "InputImage casted.";
//Cast the mask to an unsigned short image - with no respect to the incomimg maskimage
//to prevent some non-templated itk issues
typedef unsigned short LabelPixelType;
typedef itk::Image<LabelPixelType, 3 > LabelImageType;
typedef itk::CastImageFilter<ImageType, LabelImageType> MaskCastFilterType;
typename MaskCastFilterType::Pointer maskCaster = MaskCastFilterType::New();
maskCaster->SetInput(maskImage);
maskCaster->Update();
//MITK_WARN << "MaskImage casted.";
//Set all values out of the mask to (m_Min + m_Max) / 2.
typedef itk::MaskImageFilter< FloatImageType, LabelImageType, FloatImageType > MaskFilterType;
typename MaskFilterType::Pointer maskFilter = MaskFilterType::New();
maskFilter->SetInput(floatImage);
maskFilter->SetMaskImage(maskCaster->GetOutput());
maskFilter->SetOutsideValue((m_Max + m_Min) / 2);
maskFilter->Update();
//MITK_WARN << "InputImage masked.";
//Rescale intensity to match the size of the histogram
typedef itk::Image< unsigned int, 3 > OutputImageType;
typedef itk::RescaleIntensityImageFilter< FloatImageType,OutputImageType> RescalerType;
typename RescalerType::Pointer rescaler = RescalerType::New();
//We use 0 for nans, all valid numbers will be 1 < x < size
rescaler->SetOutputMinimum( 1 );
rescaler->SetOutputMaximum( size[0] );
rescaler->SetInput(maskFilter->GetOutput());
rescaler->Update();
typename OutputImageType::Pointer rescaled = rescaler->GetOutput();
//MITK_WARN << "Intensities rescaled.";
//Write back the nans because they get lost during rescaling
int xx = inputImage->GetLargestPossibleRegion().GetSize()[0];
int yy = inputImage->GetLargestPossibleRegion().GetSize()[1];
int zz = inputImage->GetLargestPossibleRegion().GetSize()[2];
for (int x = 0; x < xx; x++)
{
for (int y = 0; y < yy; y++)
{
for (int z = 0; z < zz; z++)
{
FloatImageType::IndexType indexF;
indexF[0] = x;
indexF[1] = y;
indexF[2] = z;
OutputImageType::IndexType indexO;
indexO[0] = x;
indexO[1] = y;
indexO[2] = z;
//Is Pixel NaN?
if(floatImage->GetPixel(indexF) != floatImage->GetPixel(indexF))
{
rescaled->SetPixel(indexO,0);
}
}
}
}
//All nans are now 0, the valid values are within [1,numberOfBins]
/*
OutputImageType::IndexType indexO;
indexO[0] = 0;
indexO[1] = 2;
indexO[2] = 1;
MITK_WARN << "is 0: " << rescaled->GetPixel(indexO);
indexO[0] = 0;
indexO[1] = 0;
indexO[2] = 0;
MITK_WARN << "is 1: " << rescaled->GetPixel(indexO);
*/
PixelType distanceThreshold = 1 - mitk::eps;
//Calculate the connected components
typedef itk::ScalarConnectedComponentImageFilter <OutputImageType, OutputImageType, LabelImageType >
ConnectedComponentImageFilterType;
typename ConnectedComponentImageFilterType::Pointer connected = ConnectedComponentImageFilterType::New ();
connected->SetInput(rescaled);
connected->SetMaskImage(maskCaster->GetOutput());
connected->SetDistanceThreshold(distanceThreshold);
connected->Update();
/*
indexO[0] = 0;
indexO[1] = 2;
indexO[2] = 1;
MITK_WARN << "is 0: " << (connected->GetOutput())->GetPixel(indexO);
indexO[0] = 0;
indexO[1] = 0;
indexO[2] = 0;
MITK_WARN << "is 1: " << (connected->GetOutput())->GetPixel(indexO);
MITK_WARN << "Connected components calculated.";
*/
//Relabel the components
typedef itk::RelabelComponentImageFilter <OutputImageType, OutputImageType > RelabelFilterType;
typename RelabelFilterType::Pointer relabel = RelabelFilterType::New();
typename RelabelFilterType::ObjectSizeType minSize = 1;
relabel->SetInput(connected->GetOutput());
relabel->SetMinimumObjectSize(minSize);
relabel->Update();
//MITK_WARN << "Components relabeled.";
- //Get the stats of the componentes
+ //Get the stats of the components
typedef itk::LabelStatisticsImageFilter< FloatImageType, OutputImageType> LabelStatisticsImageFilterType;
typename LabelStatisticsImageFilterType::Pointer labelStatisticsImageFilter =
LabelStatisticsImageFilterType::New();
labelStatisticsImageFilter->SetLabelInput( relabel->GetOutput() );
labelStatisticsImageFilter->SetInput(floatImage);
labelStatisticsImageFilter->UseHistogramsOn(); // needed to compute median
labelStatisticsImageFilter->Update();
/*
std::cout << "Number of labels: "
<< labelStatisticsImageFilter->GetNumberOfLabels() << std::endl;
std::cout << std::endl;
*/
typedef typename LabelStatisticsImageFilterType::ValidLabelValuesContainerType ValidLabelValuesType;
for(typename ValidLabelValuesType::const_iterator vIt = labelStatisticsImageFilter->GetValidLabelValues().begin();
vIt != labelStatisticsImageFilter->GetValidLabelValues().end();
++vIt)
{
if ( labelStatisticsImageFilter->HasLabel(*vIt) )
{
LabelPixelType labelValue = *vIt;
run[0] = labelStatisticsImageFilter->GetMean( labelValue );
run[1] = labelStatisticsImageFilter->GetCount( labelValue );
//Check for NaN and inf
if(run[0] == run[0] && !std::isinf(std::abs(run[0])))
{
output->GetIndex( run, hIndex );
output->IncreaseFrequencyOfIndex( hIndex, 1 );
/*
MITK_INFO << "Adding a region:";
MITK_INFO << "\tmin: "
<< labelStatisticsImageFilter->GetMinimum( labelValue );
MITK_INFO << "\tmax: "
<< labelStatisticsImageFilter->GetMaximum( labelValue );
MITK_INFO << "\tmean: "
<< labelStatisticsImageFilter->GetMean( labelValue );
MITK_INFO << "\tcount: "
<< labelStatisticsImageFilter->GetCount( labelValue );
*/
}
}
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetPixelValueMinMax( PixelType min, PixelType max )
{
if( this->m_Min != min || this->m_Max != max )
{
itkDebugMacro( "setting Min to " << min << "and Max to " << max );
this->m_Min = min;
this->m_Max = max;
this->Modified();
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetDistanceValueMinMax( RealType min, RealType max )
{
if( this->m_MinDistance != min || this->m_MaxDistance != max )
{
itkDebugMacro( "setting MinDistance to " << min << "and MaxDistance to "
<< max );
this->m_MinDistance = min;
this->m_MaxDistance = max;
this->Modified();
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::PrintSelf( std::ostream& os, Indent indent ) const
{
Superclass::PrintSelf( os,indent );
os << indent << "Offsets: " << this->GetOffsets() << std::endl;
os << indent << "Min: " << this->m_Min << std::endl;
os << indent << "Max: " << this->m_Max << std::endl;
os << indent << "Min distance: " << this->m_MinDistance << std::endl;
os << indent << "Max distance: " << this->m_MaxDistance << std::endl;
os << indent << "NumberOfBinsPerAxis: " << this->m_NumberOfBinsPerAxis
<< std::endl;
os << indent << "InsidePixelValue: " << this->m_InsidePixelValue << std::endl;
}
} // end of namespace Statistics
} // end of namespace itk
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h
index a3383d9723..1a45d8c9fb 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h
@@ -1,245 +1,245 @@
/*============================================================================
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.
============================================================================*/
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef __itkEnhancedScalarImageToTextureFeaturesFilter_h
#define __itkEnhancedScalarImageToTextureFeaturesFilter_h
#include "itkDataObjectDecorator.h"
#include "itkEnhancedHistogramToTextureFeaturesFilter.h"
#include "itkScalarImageToCooccurrenceMatrixFilter.h"
namespace itk
{
namespace Statistics
{
/** \class EnhancedScalarImageToTextureFeaturesFilter
* \brief This class computes texture descriptions from an image.
*
* This class computes features that summarize the texture of a given image.
* The texture features are computed a la Haralick, and have proven to be useful
* in image classification for biological and medical imaging.
* This class computes the texture features of an image (optionally in a
* masked region), averaged across several spatial directions so that they are
* invariant to rotation.
*
- * By default, texure features are computed for each spatial
+ * By default, texture features are computed for each spatial
* direction and then averaged afterward, so it is possible to access the standard
* deviations of the texture features. These values give a clue as to texture
* anisotropy. However, doing this is much more work, because it involved computing
* one GLCM for each offset given. To compute a single GLCM using the first offset ,
* call FastCalculationsOn(). If this is called, then the texture standard deviations
* will not be computed (and will be set to zero), but texture computation will
* be much faster.
*
* This class is templated over the input image type.
*
* Template Parameters:
* The image type, and the type of histogram frequency container. If you are using
* a large number of bins per axis, a sparse frequency container may be advisable.
* The default is to use a dense frequency container.
*
* Inputs and parameters:
* -# An image
* -# A mask defining the region over which texture features will be
* calculated. (Optional)
* -# The pixel value that defines the "inside" of the mask. (Optional, defaults
* to 1 if a mask is set.)
* -# The set of features to be calculated. These features are defined
* in the GreyLevelCooccurrenceMatrixTextureCoefficientsCalculator class. (Optional,
* defaults to {Energy, Entropy, InverseDifferenceMoment, Inertia, ClusterShade,
* ClusterProminence}, as in Conners, Trivedi and Harlow.)
* -# The number of intensity bins. (Optional, defaults to 256.)
* -# The set of directions (offsets) to average across. (Optional, defaults to
* {(-1, 0), (-1, -1), (0, -1), (1, -1)} for 2D images and scales analogously for ND
* images.)
* -# The pixel intensity range over which the features will be calculated.
* (Optional, defaults to the full dynamic range of the pixel type.)
*
* In general, the default parameter values should be sufficient.
*
* Outputs:
* (1) The average value of each feature.
* (2) The standard deviation in the values of each feature.
*
* Web reference:
* https://prism.ucalgary.ca/handle/1880/51900
*
* Print references:
* Haralick, R.M., K. Shanmugam and I. Dinstein. 1973. Textural Features for
* Image Classification. IEEE Transactions on Systems, Man and Cybernetics.
* SMC-3(6):610-620.
*
* Haralick, R.M. 1979. Statistical and Structural Approaches to Texture.
* Proceedings of the IEEE, 67:786-804.
*
- * R.W. Conners and C.A. Harlow. A Theoretical Comaprison of Texture Algorithms.
+ * R.W. Conners and C.A. Harlow. A Theoretical Comparison of Texture Algorithms.
* IEEE Transactions on Pattern Analysis and Machine Intelligence, 2:204-222, 1980.
*
* R.W. Conners, M.M. Trivedi, and C.A. Harlow. Segmentation of a High-Resolution
* Urban Scene using Texture Operators. Computer Vision, Graphics and Image
* Processing, 25:273-310, 1984.
*
* \sa ScalarImageToCooccurrenceMatrixFilter
* \sa HistogramToTextureFeaturesFilter
*
* Author: Zachary Pincus
* \ingroup ITKStatistics
*/
template< typename TImageType,
typename THistogramFrequencyContainer = DenseFrequencyContainer2 >
class EnhancedScalarImageToTextureFeaturesFilter:public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedScalarImageToTextureFeaturesFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro(EnhancedScalarImageToTextureFeaturesFilter, ProcessObject);
/** standard New() method support */
itkNewMacro(Self);
typedef THistogramFrequencyContainer FrequencyContainerType;
typedef TImageType ImageType;
typedef typename ImageType::Pointer ImagePointer;
typedef typename ImageType::PixelType PixelType;
typedef typename ImageType::OffsetType OffsetType;
typedef VectorContainer< unsigned char, OffsetType > OffsetVector;
typedef typename OffsetVector::Pointer OffsetVectorPointer;
typedef typename OffsetVector::ConstPointer OffsetVectorConstPointer;
typedef ScalarImageToCooccurrenceMatrixFilter<
ImageType, FrequencyContainerType > CooccurrenceMatrixFilterType;
typedef typename CooccurrenceMatrixFilterType::HistogramType HistogramType;
typedef EnhancedHistogramToTextureFeaturesFilter< HistogramType > TextureFeaturesFilterType;
typedef short TextureFeatureName;
typedef VectorContainer< unsigned char, TextureFeatureName > FeatureNameVector;
typedef typename FeatureNameVector::Pointer FeatureNameVectorPointer;
typedef typename FeatureNameVector::ConstPointer FeatureNameVectorConstPointer;
typedef VectorContainer< unsigned char, double > FeatureValueVector;
typedef typename FeatureValueVector::Pointer FeatureValueVectorPointer;
/** Smart Pointer type to a DataObject. */
typedef DataObject::Pointer DataObjectPointer;
/** Type of DataObjects used for scalar outputs */
typedef DataObjectDecorator< FeatureValueVector >
FeatureValueVectorDataObjectType;
const FeatureValueVectorDataObjectType * GetFeatureMeansOutput() const;
const FeatureValueVectorDataObjectType * GetFeatureStandardDeviationsOutput() const;
/** Connects the input image for which the features are going to be computed
*/
using Superclass::SetInput;
void SetInput(const ImageType *);
const ImageType * GetInput() const;
/** Return the feature means and deviations. */
itkGetConstReferenceObjectMacro(FeatureMeans, FeatureValueVector);
itkGetConstReferenceObjectMacro(FeatureStandardDeviations, FeatureValueVector);
/** Set the desired feature set. Optional, for default value see above. */
itkSetConstObjectMacro(RequestedFeatures, FeatureNameVector);
itkGetConstObjectMacro(RequestedFeatures, FeatureNameVector);
/** Set the offsets over which the co-occurrence pairs will be computed.
Optional; for default value see above. */
itkSetConstObjectMacro(Offsets, OffsetVector);
itkGetConstObjectMacro(Offsets, OffsetVector);
/** Set number of histogram bins along each axis.
Optional; for default value see above. */
void SetNumberOfBinsPerAxis(unsigned int);
/** Set the min and max (inclusive) pixel value that will be used for
feature calculations. Optional; for default value see above. */
void SetPixelValueMinMax(PixelType min, PixelType max);
/** Connects the mask image for which the histogram is going to be computed.
Optional; for default value see above. */
void SetMaskImage(const ImageType *);
const ImageType * GetMaskImage() const;
/** Set the pixel value of the mask that should be considered "inside" the
object. Optional; for default value see above. */
void SetInsidePixelValue(PixelType InsidePixelValue);
itkGetConstMacro(FastCalculations, bool);
itkSetMacro(FastCalculations, bool);
itkBooleanMacro(FastCalculations);
protected:
EnhancedScalarImageToTextureFeaturesFilter();
~EnhancedScalarImageToTextureFeaturesFilter() override {}
void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;
void FastCompute();
void FullCompute();
/** This method causes the filter to generate its output. */
void GenerateData() ITK_OVERRIDE;
/** Make a DataObject to be used for output output. */
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE;
private:
typename CooccurrenceMatrixFilterType::Pointer m_GLCMGenerator;
typename TextureFeaturesFilterType::Pointer m_GLCMCalculator;
FeatureValueVectorPointer m_FeatureMeans;
FeatureValueVectorPointer m_FeatureStandardDeviations;
FeatureNameVectorConstPointer m_RequestedFeatures;
OffsetVectorConstPointer m_Offsets;
bool m_FastCalculations;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedScalarImageToTextureFeaturesFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h
index d10e127e0d..8340cc5d80 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h
@@ -1,63 +1,63 @@
/*============================================================================
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 mitkGIFCooccurenceMatrix_h
#define mitkGIFCooccurenceMatrix_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFCooccurenceMatrix : public AbstractGlobalImageFeature
{
/**
- * \brief Calculates features based on the co-occurence matrix.
+ * \brief Calculates features based on the co-occurrence matrix.
*
- * This filter calculates features based on the Co-Occurence Matrix.
+ * This filter calculates features based on the Co-Occurrence Matrix.
*
* \warning{ This is a legacy class only. If possible, avoid to use it. Use
* GIFCooccurenceMatrix2 instead.}
*/
public:
mitkClassMacro(GIFCooccurenceMatrix,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFCooccurenceMatrix();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
itkGetConstMacro(Ranges, std::vector<double>);
void SetRanges(std::vector<double> ranges);
void SetRange(double range);
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureNamePart(const FeatureID& id) const override;
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
std::vector<double> m_Ranges;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h
index c4b22ed7f0..907fcb2929 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h
@@ -1,162 +1,162 @@
/*============================================================================
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 mitkGIFCooccurenceMatrix2_h
#define mitkGIFCooccurenceMatrix2_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
/**
- * \brief Calculates features based on the co-occurence matrix.
+ * \brief Calculates features based on the co-occurrence matrix.
*
- * The co-occurence matrix describes the relations between voxels in a specific direction. The elements \f$m_{i,k} \f$ of the
+ * The co-occurrence matrix describes the relations between voxels in a specific direction. The elements \f$m_{i,k} \f$ of the
* matrix count how often a voxel with the intensity \f$i \f$ has a neighbour in a certain direction with the intensity \f$ k \f$.
* The direction for each matrix is given by a directed vector \f$ \overrightarrow{d} \f$.
*
* It is important to calculate the matrices for all possible directions in order to obtain a rotation invariant feature.
- * For the 3D case, this means that there are 26 possible directions. Using the symmetrical properties of the co-occurence
+ * For the 3D case, this means that there are 26 possible directions. Using the symmetrical properties of the co-occurrence
* matrix, it is then possible to calculate the features in all directions looking at 13 different directions.
*
* The standard length of the vector is 1, e.g. looking at direct neighbours. It is possible to look at more
* distance neighbours. This is achieved using the parameter <b>range</b> which defines the distance between
* two neighbouring voxels in number of voxels. The default value for this is 1. It can be changes using the Method
* SetRange() or by passing the option <b>cooc2::range</b>.
*
* There are two possible ways of combining the information obtained from the multiple directions. The first option
* is to calculate a common matrix for all directions and then use this matrix to calculate the describing features.
* The second method is to calculate a matrix for each direction, obtain the features and then report the mean and
- * standard value of these features. Both mehtods are calcuated by this filters and reported, distinguisehd by either
+ * standard value of these features. Both methods are calculated by this filters and reported, distinguisehd by either
* an "Overall" if a single matrix is used, a "Mean" for the mean Value, or an "Std.Dev." for the standard deviation.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. The intensity used for the calculation is
* always equal to the bin number. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* This feature calculator is activated by the option <b>-cooccurence2</b> or <b>-cooc2</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
* The following features are defined. We always give the notation for the overall matrix feature
* although those for the mean and std.dev. are basically equal. In the name, \<Range\> is replace
* by the distance of the neighbours. For the definitions of the feature, the probability of each
* intensity pair (i,k) \f$ p_{i,k} = \frac{m_{i,k}}{\sum_i \sum_k m_{i,k}} \f$.
*
* In addition, the marginal sum \f$ p_{i,\cdot} = p_{\cdot,k=i} = \sum_k p_{i,k} \f$, which is
- * identical for both axis due to the symetrical nature of the matrix. Furthermore, the diagonal and
- * cross diagnoal features are used:
+ * identical for both axis due to the symmetrical nature of the matrix. Furthermore, the diagonal and
+ * cross diagonal features are used:
* \f[ p_{i-k}(l) = \sum_i \sum_k p_{i,k} \delta(l - \| i -k \| ) \enspace \enspace l = 0, \dots, N_g -1 \f]
* \f[ p_{i+k}(l) = \sum_i \sum_k p_{i,k} \delta(l - ( i + k ) ) \enspace \enspace l = 2, \dots, 2 N_g \f]
* Here, \f$ \delta(x) \f$ is the dirac function, which is one for \f$x=0 \f$ and zero otherwise.
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Maximum</b>:
* \f[ \textup{Joint Maximum}= \textup{max}(p_{i,k}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Average</b>:
* \f[ \textup{Joint Average} = \mu_{ja} = \sum_i \sum_k i p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Variance</b>:
* \f[ \textup{Joint Variance} = \sum_i \sum_k (i - \mu_{ja})^2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Entropy</b>:
* \f[ \textup{Joint Entropy} = e_j = - \sum_i \sum_k p_{i,k} \textup{log}_2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Maximum</b>:
* \f[ \textup{Row Maximum}= \textup{max}(p_{i,\cdot}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Average</b>:
* \f[ \textup{Row Average} = \mu_{ra} = \sum_i i p_{i,\cdot} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Variance</b>:
* \f[ \textup{Row Variance} = \sigma^2_{i, \cdot} = \sum_i (i - \mu_{ra})^2 p_{i,\cdot} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Entropy</b>:
* \f[ \textup{Row Entropy} = e_r = - \sum_i p_{i,\cdot} \textup{log}_2 p_{i,\cdot} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall First Row-Column Entropy</b>:
* \f[ \textup{First Row-Column Entropy} = e_1 = - \sum_i \sum_k p_{i,k} \textup{log}_2 ( p_{i,\cdot} p_{\cdot,k}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Second Row-Column Entropy</b>:
* \f[ \textup{Second Row-Column Entropy} = e_2 = - \sum_i \sum_k p_{i,\cdot} p_{\cdot,k} \textup{log}_2 ( p_{i,\cdot} p_{\cdot,k}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Difference Average</b>:
* \f[ \textup{Difference Average} = \mu_{da} = \sum_l l p_{i-k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Difference Variance</b>:
* \f[ \textup{Difference Variance} = \sum_l (i - \mu_{da})^2 p_{i-k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Difference Entropy</b>:
* \f[ \textup{Difference Entropy} = - \sum_l p_{i-k}(l) \textup{log}_2 p_{i-k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Sum Average</b>:
* \f[ \textup{Sum Average} = \mu_{sa} = \sum_l l p_{i+k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Sum Variance</b>:
* \f[ \textup{Sum Variance} = \sum_l (i - \mu_{sa})^2 p_{i+k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Sum Entropy</b>:
* \f[ \textup{Sum Entropy} = - \sum_l p_{i+k}(l) \textup{log}_2 p_{i+k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Angular Second Moment</b>:
* \f[ \textup{Angular Second Moment} = \sum_i \sum_k p^2_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Contrast</b>:
* \f[ \textup{Contrast} = \sum_i \sum_k (i-k)^2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Dissimilarity</b>:
* \f[ \textup{Dissimilarity} = \sum_i \sum_k \| i-k\| p^2_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference</b>:
* \f[ \textup{Inverse Difference} = \sum_i \sum_k \frac{p_{i,k}}{1+\| i-k\|} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference Normalized</b>:
* \f[ \textup{Inverse Difference Normalized} = \sum_i \sum_k \frac{p_{i,k}}{1+\frac{\| i-k\|}{N_g}} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference Moment</b>:
* \f[ \textup{Inverse Difference Moment} = \sum_i \sum_k \frac{p_{i,k}}{1+ ( i-k )^2} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference Moment Normalized</b>:
* \f[ \textup{Inverse Difference Moment Normalized} = \sum_i \sum_k \frac{p_{i,k}}{1+\frac{( i-k ) ^2}{N_g}} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Variance</b>:
* \f[ \textup{Inverse Difference Moment Normalized} = \sum_i \sum_k \frac{p_{i,k}}{(i-k)^2} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Correlation</b>:
* \f[ \textup{Correlation} = \frac{1}{\sigma^2_{i,\cdot}} \sum_i \sum_k (i - \mu_{ra})(k - \mu_{ra}) p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Autocorrelation</b>:
* \f[ \textup{Autocorrelation} = \sum_i \sum_k i k p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Cluster Tendency</b>:
* \f[ \textup{Cluster Tendency} = \sum_i \sum_k (i + k - 2\mu_{ra})^2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Cluster Shade</b>:
* \f[ \textup{Cluster Shade} = \sum_i \sum_k (i + k - 2\mu_{ra})^3 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Cluster Prominence</b>:
* \f[ \textup{Cluster Prominence} = \sum_i \sum_k (i + k - 2\mu_{ra})^4 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall First Measure of Information Correlation</b>:
* \f[ \textup{First Measure of Information Correlation} = \frac{ e_j- e_1}{e_r} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Second Measure of Information Correlation</b>:
* \f[ \textup{Second Measure of Information Correlation} = \sqrt{1- \exp(-2 (e_2 - e_j)} \f]
*/
class MITKCLUTILITIES_EXPORT GIFCooccurenceMatrix2 : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFCooccurenceMatrix2, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFCooccurenceMatrix2();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
itkGetConstMacro(Ranges, std::vector<double>);
void SetRanges(std::vector<double> ranges);
void SetRange(double range);
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
std::vector<double> m_Ranges;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h b/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h
index 601d9a1d45..cc87ba77d3 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h
@@ -1,98 +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 mitkGIFCurvatureStatistic_h
#define mitkGIFCurvatureStatistic_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
- * \brief Calculates features based on the co-occurence matrix.
+ * \brief Calculates features based on the co-occurrence matrix.
*
* The Curvature is a measure for the bending of a surface and is therefore a measure for the description of the
* surface of an segmentation.
*
* THe curvature is calculated for each point of the surface of the given object and then a combined measure is
* produced. It measures the divergence of the orientation of an curve from the
* tangent of the curve. There are multiple ways to calculate the Curvature:
*
* <b> Gaussian Curvature</b>: The discrete gaussian curvature (K) is computed as \f$K(\textup{Corner Point v}) = 2 * \pi - \sum_{\textup{Neighoubring Voxel Surfaces f of v}} (\textup{Angle}_f \textup{at} v) \f$.
* <b> Mean Curvature</b>:The mean curvature (H) is computed as \f$H(\textup{Corner Point v}) = \textup{average over edges e neighbouring v of H(e)} \f$.
* with \f$H(edge e) = length(e)*dihedral_angle(e)\f$
* <b>Maximum</b> (\f$k_max\f$) and <b>Minimum</b> (\f$k_min\f$) Principal Curvatures
* \f$k_max = H + sqrt(H^2 - K)\f$
* \f$k_min = H - sqrt(H^2 - K)\f$
* Excepting spherical and planar surfaces which have equal principal curvatures,
* the curvature at a point on a surface varies with the direction one "sets off"
* from the point. For all directions, the curvature will pass through two extrema:
* a minimum (\f$k_min\f$) and a maximum (\f$k_max\f$) which occur at mutually orthogonal
* directions to each other.
*
* This method does not take any parameters.
*
* This feature calculator is activated by the option <b>-curvature</b> or <b>-cur</b>.
*
* The features are calculated based on a mask, which is converted into a mesh.
*
* The following features are defined. All features are calculated for all four possible
* curvation calculation methods (Gaussian, Mean, Minimum, Maximum). The principal way
* of calculating these features is the same, the used curvation is indicated by \<name\> in the
* feature name:
*
* - <b>Curvature Feature::Minimum \<name\> Curvature</b>:
* The minimum curvature for the whole given mask
* - <b>Curvature Feature::Maximum \<name\> Curvature</b>:
* The maximum curvature for the whole given mask
* - <b>Curvature Feature::Mean \<name\> Curvature</b>:
* The mean curvature for the whole given mask
* - <b>Curvature Feature::Standard Deviation \<name\> Curvature</b>:
* The standard deviation curvature for the whole given mask
* - <b>Curvature Feature::Skewness \<name\> Curvature</b>:
* The skewness curvature for the whole given mask
* - <b>Curvature Feature::Mean Positive \<name\> Curvature</b>:
* The mean curvature of all positive curvatures from the whole given mask
* - <b>Curvature Feature::Standard Deviation Positive \<name\> Curvature</b>:
* The Standard Deviation curvature of all positive curvatures from the whole given mask
* - <b>Curvature Feature::Skewness Positive \<name\> Curvature</b>:
* The Skewness curvature of all positive curvatures from the whole given mask
* - <b>Curvature Feature::Mean Negative \<name\> Curvature</b>:
* The mean curvature of all Negative curvatures from the whole given mask
* - <b>Curvature Feature::Standard Deviation Negative \<name\> Curvature</b>:
* The Standard Deviation curvature of all Negative curvatures from the whole given mask
* - <b>Curvature Feature::Skewness Negative \<name\> Curvature</b>:
* The Skewness curvature of all Negative curvatures from the whole given mask
*/
class MITKCLUTILITIES_EXPORT GIFCurvatureStatistic : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFCurvatureStatistic,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFCurvatureStatistic();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser &parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h
index 61d9682dfe..c86a2b53bc 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h
@@ -1,109 +1,109 @@
/*============================================================================
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 mitkGIFFirstOrderHistogramStatistics_h
#define mitkGIFFirstOrderHistogramStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
- * \brief Calulates first order features based on a histogram.
+ * \brief Calculates first order features based on a histogram.
*
* This class can be used to calculate first order features based on a histogram.
* For each feature, two variations are given, once the value of the feature that is
* obtained if the mean intensity of the histogram bins is used and the
* histogram bin that corresponds to the feature value. See AbstractGlobalImageFeature for more
* information on the histogram initialization. The histogram gives a probability \f$p_i\f$ for the
* intensity \f$x_i\f$ that is linked to the bin \f$i\f$. The histogram bins start at index 1.
*
* This feature calculator is activated by the option "<b>-first-order-histogram</b>" or "<b>-foh</b>".
* Beside the options for the histogram definitions, which are given in the description of AbstractGlobalImageFeature , no
* additional parameters are available.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of 1
* are treated as masked.
*
* The resulting features are:
- * - <b>First Order Histogram::Mean Value</b>: The mean intensity of all voxels, calulated by \f$ \mu_x = \sum p_i x_i\f$.
+ * - <b>First Order Histogram::Mean Value</b>: The mean intensity of all voxels, calculated by \f$ \mu_x = \sum p_i x_i\f$.
* - <b>First Order Histogram::Variance Value</b> The variance intensity is calculated as : \f$ \sigma^2 = \sum p_i (x_i - \mu_x)^2\f$.
* - <b>First Order Histogram::Skewness Value</b>: \f[ skewness = \frac{\sum p_i (x_i - \mu_x)^3}{\sigma^3} \f]
* - <b>First Order Histogram::Excess Kurtosis Value</b>: \f[ skewness = \frac{\sum p_i (x_i - \mu_x)^4}{\sigma^4} - 3 \f]
* - <b>First Order Histogram::Median Value</b>: The median intensity value based on the histogram values.
* - <b>First Order Histogram::Minimum Value</b>: The minimum observed intensity value.
* - <b>First Order Histogram::Percentile 10 Value</b>: The intensity that is equal or greater than 10% of all observed intensities.
* - <b>First Order Histogram::Percentile 90 Value</b>: The intensity that is equal or greater than 90% of all observed intensities.
* - <b>First Order Histogram::Maximum Value</b>: The maximum observerd intensity value.
* - <b>First Order Histogram::Mode Value</b>: The most common intensity value, i.e. the value of the bin with the highest probability.
* - <b>First Order Histogram::Interquantile Range Value</b>: The intensity difference between Percentile 75% (\f$ P75\f$) and Percentile 25% (\f$ P25\f$).
* - <b>First Order Histogram::Range Value</b>: The difference between the observed maximum and minimum intensity.
* - <b>First Order Histogram::Mean Absolute Deviation Value</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (x_i - \mu_x) \right \| \f]
* - <b>First Order Histogram::Robust Mean Value</b>: The mean of all intensities between the 10% and 90% quantile.
* - <b>First Order Histogram::Robust Mean Absolute Deviation Value</b>: The Mean absolute deviation for all values between the 10% and 90% quantile. It is based on the robust mean value.
* - <b>First Order Histogram::Median Absolute Deviation Value</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (x_i - \textup{median}) \right \| \f]
* - <b>First Order Histogram::Coefficient of Variation Value</b>: \f[ \frac{\sigma_x}{\mu_x} \f]
* - <b>First Order Histogram::Quantile coefficient of Dispersion Value</b>: \f[ \textup{Quantile coefficient of Dispersion} = \frac{P75 - P25}{P75 + P25} \f]
* - <b>First Order Histogram::Entropy Value</b>: The entropy is only based on histogram bins with a probability greater than 0.0000001: \f[ \textup{entropy} = - \sum p_i \textup{log}_2 p_i \f]
* - <b>First Order Histogram::Uniformity Value</b>: \f$ \sum p_i^2 \f$
- * - <b>First Order Histogram::Mean Index</b>: The mean index of all voxels, calulated by \f$ \mu_i = \sum p_i i\f$.
+ * - <b>First Order Histogram::Mean Index</b>: The mean index of all voxels, calculated by \f$ \mu_i = \sum p_i i\f$.
* - <b>First Order Histogram::Variance Index</b>: The variance index is calculated as : \f$ \sigma_i^2 = \sum p_i (i - \mu_i)^2\f$.
* - <b>First Order Histogram::Skewness Index</b>: \f[ skewness = \frac{\sum p_i (i - \mu_i)^3}{\sigma_i^3} \f]
* - <b>First Order Histogram::Excess Kurtosis Index</b>: \f[ skewness = \frac{\sum p_i (i - \mu_i)^4}{\sigma_i^4} - 3 \f]
* - <b>First Order Histogram::Median Index</b>: The median index value based on the histogram values.
* - <b>First Order Histogram::Minimum Index</b>: The index of the minimum observed intensity value.
* - <b>First Order Histogram::Percentile 10 Index</b>: The index oft the intensity that is equal or greater than 10% of all observed intensities.
* - <b>First Order Histogram::Percentile 90 Index</b>: The index of the intensity that is equal or greater than 90% of all observed intensities.
* - <b>First Order Histogram::Maximum Index</b>: The index of the maximum observerd intensity value.
* - <b>First Order Histogram::Mode Index</b>: The index of the most common intensity value, i.e. the index of the bin with the highest probability.
* - <b>First Order Histogram::Interquantile Range Index</b>: The index difference between Percentile 75% (\f$ P75\f$) and Percentile 25% (\f$ P25\f$).
* - <b>First Order Histogram::Range Index</b>: The index difference between the index of the observed maximum and minimum intensity.
* - <b>First Order Histogram::Mean Absolute Deviation Index</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (i - \mu_i) \right \| \f]
* - <b>First Order Histogram::Robust Mean Absolute Deviation Index</b>: The Mean absolute deviation for all values between the 10% and 90% quantile. It is based on the robust mean value.
* - <b>First Order Histogram::Median Absolute Deviation Index</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (i - \textup{median}) \right \| \f]
* - <b>First Order Histogram::Coefficient of Variation Index</b>: \f[ \frac{\sigma_i}{\mu_i} \f]
* - <b>First Order Histogram::Quantile coefficient of Dispersion Index</b>: \f[ \textup{Quantile coefficient of Dispersion} = \frac{P75 - P25}{P75 + P25} \f]
* - <b>First Order Histogram::Entropy Index</b>: The entropy is only based on histogram bins with a probability greater than 0.0000001: \f$ \textup{entropy} = - \sum p_i \textup{log}_2 p_i \f$. Note that this is the same as the entropy value.
* - <b>First Order Histogram::Uniformity Index</b>: \f$ \sum p_i^2 \f$. Note that this is the same as the uniformity value.
- * - <b>First Order Histogram::Maximum Gradient</b>: The maximum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calulation.
+ * - <b>First Order Histogram::Maximum Gradient</b>: The maximum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calculation.
* - <b>First Order Histogram::Maximum Gradient Index</b>: The index of the bin that belongs to the maximum gradient.
- * - <b>First Order Histogram::Minimum Gradient</b>: The minimum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calulation.
+ * - <b>First Order Histogram::Minimum Gradient</b>: The minimum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calculation.
* - <b>First Order Histogram::Minimum Gradient Index</b>:The index of the bin that belongs to the minimum gradient.
* - <b>First Order Histogram::Robust Mean Index</b>: The mean index of all intensities between the 10% and 90% quantile.
* - <b>First Order Histogram::Number of Bins</b>: The number of bins in the histogram. This is rather for control, as this parameter is likely to be determined by the configuration rather than the image.
* - <b>First Order Histogram::Bin Size</b>: The binsize of the bins from the histogram. This is rather for control, as this parameter is likely to be determined by the configuration rather than the image.
*/
class MITKCLUTILITIES_EXPORT GIFFirstOrderHistogramStatistics : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFFirstOrderHistogramStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFFirstOrderHistogramStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h
index 87f2660c49..7e79804a95 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h
@@ -1,146 +1,146 @@
/*============================================================================
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 mitkGIFFirstOrderNumericStatistics_h
#define mitkGIFFirstOrderNumericStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFFirstOrderNumericStatistics : public AbstractGlobalImageFeature
{
public:
/**
* \brief Calculates first order statistics of the given image.
*
* The first order statistics for the intensity distribution within a given Region of Interest (ROI)
- * is caluclated. The ROI is defined using a mask.
+ * is calculated. The ROI is defined using a mask.
*
* The features are calculated on a quantified image. If the bin-size is too big, the obtained values
- * can be errornous and missleading. It is therefore important to use enough bins. The binned approach is
+ * can be erroneous and misleading. It is therefore important to use enough bins. The binned approach is
* used in order to avoid floating-point related errors.
*
* This feature calculator is activated by the option <b>-first-order</b> or <b>-fo</b>.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
* The following features are then defined using the (binned) voxel intensity \f$ x_i \f$ of each voxel, the probability
* an intensity \f$ p_x \f$, and the overall number of voxels within the mask \f$ N_v \f$:
* - <b>First Order::Mean</b>: The mean intensity within the ROI
* \f[ \textup{Mean}= \mu = \frac{1}{N_v} \sum x_i \f]
* - <b>First Order::Unbiased Variance</b>: An unbiased estimation of the variance:
* \f[ \textup{Unbiased Variance} = \frac{1}{N_v - 1} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Biased Variance</b>: An biased estimation of the variance. If not specified otherwise, this is
* used as the variance:
* \f[ \textup{Biased Variance} = \sigma^2 = \frac{1}{N_v} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Unbiased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Unbiased Standard Deviation} = \sqrt{\frac{1}{N_v-1} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Biased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Biased Standard Deviation} = \sigma = \sqrt{\frac{1}{N_v} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Skewness</b>:
* \f[ \textup{Skewness} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^3}{\sigma^3} \f]
* - <b>First Order::Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution:
+ * distribution:
* \f[ \textup{Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} \f]
* - <b>First Order::Excess Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
+ * distribution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
* ensuring that a gaussian distribution has an excess kurtosis of 0.
* \f[ \textup{Excess Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} - 3 \f]
* - <b>First Order::Median</b>: The median is defined as the median of the all intensities in the ROI.
* - <b>First Order::Minimum</b>: The minimum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::05th Percentile</b>: \f$ P_{5\%} \f$ The 5% percentile. 5% of all voxel do have this or a lower intensity.
* - <b>First Order::10th Percentile</b>: \f$ P_{10\%} \f$ The 10% percentile. 10% of all voxel do have this or a lower intensity.
* - <b>First Order::15th Percentile</b>: \f$ P_{15\%} \f$ The 15% percentile. 15% of all voxel do have this or a lower intensity.
* - <b>First Order::20th Percentile</b>: \f$ P_{20\%} \f$ The 20% percentile. 20% of all voxel do have this or a lower intensity.
* - <b>First Order::25th Percentile</b>: \f$ P_{25\%} \f$ The 25% percentile. 25% of all voxel do have this or a lower intensity.
* - <b>First Order::30th Percentile</b>: \f$ P_{30\%} \f$ The 30% percentile. 30% of all voxel do have this or a lower intensity.
* - <b>First Order::35th Percentile</b>: \f$ P_{35\%} \f$ The 35% percentile. 35% of all voxel do have this or a lower intensity.
* - <b>First Order::40th Percentile</b>: \f$ P_{40\%} \f$ The 40% percentile. 40% of all voxel do have this or a lower intensity.
* - <b>First Order::45th Percentile</b>: \f$ P_{45\%} \f$ The 45% percentile. 45% of all voxel do have this or a lower intensity.
* - <b>First Order::50th Percentile</b>: \f$ P_{50\%} \f$ The 50% percentile. 50% of all voxel do have this or a lower intensity.
* - <b>First Order::55th Percentile</b>: \f$ P_{55\%} \f$ The 55% percentile. 55% of all voxel do have this or a lower intensity.
* - <b>First Order::60th Percentile</b>: \f$ P_{60\%} \f$ The 60% percentile. 60% of all voxel do have this or a lower intensity.
* - <b>First Order::65th Percentile</b>: \f$ P_{65\%} \f$ The 65% percentile. 65% of all voxel do have this or a lower intensity.
* - <b>First Order::70th Percentile</b>: \f$ P_{70\%} \f$ The 70% percentile. 70% of all voxel do have this or a lower intensity.
* - <b>First Order::75th Percentile</b>: \f$ P_{75\%} \f$ The 75% percentile. 75% of all voxel do have this or a lower intensity.
* - <b>First Order::80th Percentile</b>: \f$ P_{80\%} \f$ The 80% percentile. 80% of all voxel do have this or a lower intensity.
* - <b>First Order::85th Percentile</b>: \f$ P_{85\%} \f$ The 85% percentile. 85% of all voxel do have this or a lower intensity.
* - <b>First Order::90th Percentile</b>: \f$ P_{90\%} \f$ The 90% percentile. 90% of all voxel do have this or a lower intensity.
* - <b>First Order::95th Percentile</b>: \f$ P_{95\%} \f$ The 95% percentile. 95% of all voxel do have this or a lower intensity.
* - <b>First Order::Maximum</b>: The maximum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::Range</b>: The range of intensity values is defined as the difference between the maximum
* and minimum intensity in the ROI.
* - <b>First Order::Interquartile Range</b>: The difference between the 75% and 25% quantile.
* - <b>First Order::Mean Absolute Deviation</b>: The mean absolute deviation gives the mean distance of each
- * voxel intensity to the overal mean intensity and is a measure of the dispersion of the intensity form the
+ * voxel intensity to the overall mean intensity and is a measure of the dispersion of the intensity form the
* mean value:
* \f[ \textup{Mean Absolute Deviation} = \frac{1}{N_v} \sum \left \| x_i - \mu \right \| \f]
* - <b>First Order::Robust Mean</b>: The mean intensity within the ROI for all voxels between the 10% and 90% quantile:
* \f[ \textup{Robust Mean}= \mu_R = \frac{1}{N_{vr}} \sum x_i \f]
* - <b>First Order::Robust Mean Absolute Deviation</b>: The absolute deviation of all intensities within the ROI for
* all voxels between the 10% and 90% quantilefrom the robust mean intensity:
* \f[ \textup{Robust Mean Absolute Deviation}= \mu_R = \frac{1}{N_{vr}} \sum \left \| x_i - \mu_R \right \| \f]
* - <b>First Order::Median Absolute Deviation</b>: Similar to the mean absolute deviation, but uses the median
* instead of the mean to measure the center of the distribution.
* - <b>First Order::Coefficient Of Variation</b>: Measures the dispersion of the intensity distribution:
* \f[ \textup{Coefficient Of Variation} = \frac{sigma}{\mu} \f]
- * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to teh coefficient of variance:
+ * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to the coefficient of variance:
* \f[ \textup{Quantile Coefficient Of Dispersion} = \frac{P_{75\%} - P_{25\%} }{P_{75\%} + P_{25\%}} \f]
* - <b>First Order::Energy</b>: The intensity energy:
* \f[ \textup{Energy} = \sum x_i ^2 \f]
* - <b>First Order::Root Mean Square</b>: Root mean square is an important measure for the error.
* \f[ \textup{Root Mean Square} = \sqrt{\frac{\sum x_i ^2}{N_v}} \f]
* - <b>First Order::Uniformity</b>:
* \f[ \textup{Uniformity} = \sum p_x^2 \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Covered Image Intensity Range</b>: Percentage of the image intensity range (maximum - minimum in whole
* image) that is covered by the ROI.
* - <b>First Order::Sum</b>: The sum of all intensities. It is correlated to the mean intensity.
* \f[ \textup{Sum} = \sum x_i \f]
* - <b>First Order::Mode</b>: The most common intensity.
* - <b>First Order::Mode Probability</b>: The likelihood of the most common intensity.
* - <b>First Order::Number Of Voxels</b>: \f$ N_v \f$ the number of voxels covered by the ROI.
* - <b>First Order::Image Dimension</b>: The dimensionality of the image (e.g. 2D, 3D, etc.).
* - <b>First Order::Number Of Voxels</b>: The product of all spacing along all dimensions. In 3D, this is equal to the
* volume.
* - <b>First Order::Number Of Voxels</b>: The volume of a single voxel. If the dimensionality is only 2D, this is the
* surface of an voxel.
*/
mitkClassMacro(GIFFirstOrderNumericStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFFirstOrderNumericStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h
index 6c913ba0a2..c49a0425cf 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h
@@ -1,145 +1,145 @@
/*============================================================================
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 mitkGIFFirstOrderStatistics_h
#define mitkGIFFirstOrderStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFFirstOrderStatistics : public AbstractGlobalImageFeature
{
public:
/**
* \brief Calculates first order statistics of the given image.
*
* The first order statistics for the intensity distribution within a given Region of Interest (ROI)
- * is caluclated. The ROI is defined using a mask.
+ * is calculated. The ROI is defined using a mask.
*
* The features are calculated on a quantified image. If the bin-size is too big, the obtained values
- * can be errornous and missleading. It is therefore important to use enough bins. The binned approach is
+ * can be erroneous and misleading. It is therefore important to use enough bins. The binned approach is
* used in order to avoid floating-point related errors.
*
* This feature calculator is activated by the option <b>-first-order</b> or <b>-fo</b>.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
* The following features are then defined using the (binned) voxel intensity \f$ x_i \f$ of each voxel, the probability
* an intensity \f$ p_x \f$, and the overall number of voxels within the mask \f$ N_v \f$:
* - <b>First Order::Mean</b>: The mean intensity within the ROI
* \f[ \textup{Mean}= \mu = \frac{1}{N_v} \sum x_i \f]
* - <b>First Order::Unbiased Variance</b>: An unbiased estimation of the variance:
* \f[ \textup{Unbiased Variance} = \frac{1}{N_v - 1} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Biased Variance</b>: An biased estimation of the variance. If not specified otherwise, this is
* used as the variance:
* \f[ \textup{Biased Variance} = \sigma^2 = \frac{1}{N_v} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Unbiased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Unbiased Standard Deviation} = \sqrt{\frac{1}{N_v-1} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Biased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Biased Standard Deviation} = \sigma = \sqrt{\frac{1}{N_v} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Skewness</b>:
* \f[ \textup{Skewness} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^3}{\sigma^3} \f]
* - <b>First Order::Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution:
+ * distribution:
* \f[ \textup{Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} \f]
* - <b>First Order::Excess Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
+ * distribution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
* ensuring that a gaussian distribution has an excess kurtosis of 0.
* \f[ \textup{Excess Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} - 3 \f]
* - <b>First Order::Median</b>: The median is defined as the median of the all intensities in the ROI.
* - <b>First Order::Minimum</b>: The minimum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::05th Percentile</b>: \f$ P_{5\%} \f$ The 5% percentile. 5% of all voxel do have this or a lower intensity.
* - <b>First Order::10th Percentile</b>: \f$ P_{10\%} \f$ The 10% percentile. 10% of all voxel do have this or a lower intensity.
* - <b>First Order::15th Percentile</b>: \f$ P_{15\%} \f$ The 15% percentile. 15% of all voxel do have this or a lower intensity.
* - <b>First Order::20th Percentile</b>: \f$ P_{20\%} \f$ The 20% percentile. 20% of all voxel do have this or a lower intensity.
* - <b>First Order::25th Percentile</b>: \f$ P_{25\%} \f$ The 25% percentile. 25% of all voxel do have this or a lower intensity.
* - <b>First Order::30th Percentile</b>: \f$ P_{30\%} \f$ The 30% percentile. 30% of all voxel do have this or a lower intensity.
* - <b>First Order::35th Percentile</b>: \f$ P_{35\%} \f$ The 35% percentile. 35% of all voxel do have this or a lower intensity.
* - <b>First Order::40th Percentile</b>: \f$ P_{40\%} \f$ The 40% percentile. 40% of all voxel do have this or a lower intensity.
* - <b>First Order::45th Percentile</b>: \f$ P_{45\%} \f$ The 45% percentile. 45% of all voxel do have this or a lower intensity.
* - <b>First Order::50th Percentile</b>: \f$ P_{50\%} \f$ The 50% percentile. 50% of all voxel do have this or a lower intensity.
* - <b>First Order::55th Percentile</b>: \f$ P_{55\%} \f$ The 55% percentile. 55% of all voxel do have this or a lower intensity.
* - <b>First Order::60th Percentile</b>: \f$ P_{60\%} \f$ The 60% percentile. 60% of all voxel do have this or a lower intensity.
* - <b>First Order::65th Percentile</b>: \f$ P_{65\%} \f$ The 65% percentile. 65% of all voxel do have this or a lower intensity.
* - <b>First Order::70th Percentile</b>: \f$ P_{70\%} \f$ The 70% percentile. 70% of all voxel do have this or a lower intensity.
* - <b>First Order::75th Percentile</b>: \f$ P_{75\%} \f$ The 75% percentile. 75% of all voxel do have this or a lower intensity.
* - <b>First Order::80th Percentile</b>: \f$ P_{80\%} \f$ The 80% percentile. 80% of all voxel do have this or a lower intensity.
* - <b>First Order::85th Percentile</b>: \f$ P_{85\%} \f$ The 85% percentile. 85% of all voxel do have this or a lower intensity.
* - <b>First Order::90th Percentile</b>: \f$ P_{90\%} \f$ The 90% percentile. 90% of all voxel do have this or a lower intensity.
* - <b>First Order::95th Percentile</b>: \f$ P_{95\%} \f$ The 95% percentile. 95% of all voxel do have this or a lower intensity.
* - <b>First Order::Maximum</b>: The maximum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::Range</b>: The range of intensity values is defined as the difference between the maximum
* and minimum intensity in the ROI.
* - <b>First Order::Interquartile Range</b>: The difference between the 75% and 25% quantile.
* - <b>First Order::Mean Absolute Deviation</b>: The mean absolute deviation gives the mean distance of each
- * voxel intensity to the overal mean intensity and is a measure of the dispersion of the intensity form the
+ * voxel intensity to the overall mean intensity and is a measure of the dispersion of the intensity form the
* mean value:
* \f[ \textup{Mean Absolute Deviation} = \frac{1}{N_v} \sum \left \| x_i - \mu \right \| \f]
* - <b>First Order::Robust Mean</b>: The mean intensity within the ROI for all voxels between the 10% and 90% quantile:
* \f[ \textup{Robust Mean}= \mu_R = \frac{1}{N_{vr}} \sum x_i \f]
* - <b>First Order::Robust Mean Absolute Deviation</b>: The absolute deviation of all intensities within the ROI for
* all voxels between the 10% and 90% quantilefrom the robust mean intensity:
* \f[ \textup{Robust Mean Absolute Deviation}= \mu_R = \frac{1}{N_{vr}} \sum \left \| x_i - \mu_R \right \| \f]
* - <b>First Order::Median Absolute Deviation</b>: Similar to the mean absolute deviation, but uses the median
* instead of the mean to measure the center of the distribution.
* - <b>First Order::Coefficient Of Variation</b>: Measures the dispersion of the intensity distribution:
* \f[ \textup{Coefficient Of Variation} = \frac{sigma}{\mu} \f]
- * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to teh coefficient of variance:
+ * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to the coefficient of variance:
* \f[ \textup{Quantile Coefficient Of Dispersion} = \frac{P_{75\%} - P_{25\%} }{P_{75\%} + P_{25\%}} \f]
* - <b>First Order::Energy</b>: The intensity energy:
* \f[ \textup{Energy} = \sum x_i ^2 \f]
* - <b>First Order::Root Mean Square</b>: Root mean square is an important measure for the error.
* \f[ \textup{Root Mean Square} = \sqrt{\frac{\sum x_i ^2}{N_v}} \f]
* - <b>First Order::Uniformity</b>:
* \f[ \textup{Uniformity} = \sum p_x^2 \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Covered Image Intensity Range</b>: Percentage of the image intensity range (maximum - minimum in whole
* image) that is covered by the ROI.
* - <b>First Order::Sum</b>: The sum of all intensities. It is correlated to the mean intensity.
* \f[ \textup{Sum} = \sum x_i \f]
* - <b>First Order::Mode</b>: The most common intensity.
* - <b>First Order::Mode Probability</b>: The likelihood of the most common intensity.
* - <b>First Order::Number Of Voxels</b>: \f$ N_v \f$ the number of voxels covered by the ROI.
* - <b>First Order::Image Dimension</b>: The dimensionality of the image (e.g. 2D, 3D, etc.).
* - <b>First Order::Number Of Voxels</b>: The product of all spacing along all dimensions. In 3D, this is equal to the
* volume.
* - <b>First Order::Number Of Voxels</b>: The volume of a single voxel. If the dimensionality is only 2D, this is the
* surface of an voxel.
*/
mitkClassMacro(GIFFirstOrderStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFFirstOrderStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h
index 019795f32e..20a792f609 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h
@@ -1,168 +1,168 @@
/*============================================================================
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 mitkGIFGreyLevelDistanceZone_h
#define mitkGIFGreyLevelDistanceZone_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
struct GreyLevelDistanceZoneFeatures
{
GreyLevelDistanceZoneFeatures() :
SmallDistanceEmphasis(0),
LargeDistanceEmphasis(0),
LowGreyLevelEmphasis(0),
HighGreyLevelEmphasis(0),
SmallDistanceLowGreyLevelEmphasis(0),
SmallDistanceHighGreyLevelEmphasis(0),
LargeDistanceLowGreyLevelEmphasis(0),
LargeDistanceHighGreyLevelEmphasis(0),
GreyLevelNonUniformity(0),
GreyLevelNonUniformityNormalized(0),
ZoneDistanceNonUniformity(0),
ZoneDistanceNoneUniformityNormalized(0),
ZonePercentage(0),
GreyLevelMean(0),
GreyLevelVariance(0),
ZoneDistanceMean(0),
ZoneDistanceVariance(0),
ZoneDistanceEntropy(0)
{
}
public:
double SmallDistanceEmphasis;
double LargeDistanceEmphasis;
double LowGreyLevelEmphasis;
double HighGreyLevelEmphasis;
double SmallDistanceLowGreyLevelEmphasis;
double SmallDistanceHighGreyLevelEmphasis;
double LargeDistanceLowGreyLevelEmphasis;
double LargeDistanceHighGreyLevelEmphasis;
double GreyLevelNonUniformity;
double GreyLevelNonUniformityNormalized;
double ZoneDistanceNonUniformity;
double ZoneDistanceNoneUniformityNormalized;
double ZonePercentage;
double GreyLevelMean;
double GreyLevelVariance;
double ZoneDistanceMean;
double ZoneDistanceVariance;
double ZoneDistanceEntropy;
};
class MITKCLUTILITIES_EXPORT GIFGreyLevelDistanceZone : public AbstractGlobalImageFeature
{
/**
* \brief Calculates the Grey Level Distance Zone
*
* This class can be used to calculate Grey Level Distance Zone as presented in Thibault et al. 2014.
*
* The basic idea behind the Grey Level Distance Zone based features is to count the connected areas
* with a given intensity value \f$x_i\f$ and a given distance to the border of each segmentation \f$d_i\f$.
- * Several features are then calculated based on a matrix, that gives the number of occurence for each
+ * Several features are then calculated based on a matrix, that gives the number of occurrence for each
* combination of \f$x_i\f$ and \f$ d_i \f$ as \f$m_{x,d}\f$.
*
* This feature calculator is activated by the option <b>-grey-level-distance-zone</b> or <b>-gldz</b>.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. It is expected that the image contains only voxels with value 0 and 1,
* of which all voxels with an value equal to one are treated as masked.
*
* The features depend on the distance to the border of the segmentation ROI. In some cases, the border
* definition might be different from the definition of the masked area, for example, if small openings
* in the mask should not influence the distance. Due to that, it is possible to submit a second mask,
* named Morphological Mask to the features that is then used to calculate the distance of each voxel to
* border of the segmented area. The morpological mask can be either set by the function call SetMorphMask()
* or by the corresponding global option. (Not parsed by the filter itself, but by the command line tool).
*
* Beside the complete matrix, which is represented by its individual elements \f$m_{x,d}\f$, som eadditional
* values are used for the definition. \f$N_g\f$ is the number of discrete grey levels, \f$ N_d\f$ the number
* (or maximum value) of possible distances, and \f$N_s\f$ the total number of zones.
* \f$m_{x,d}\f$ gives the number of connected areas with the discrete
* grey level x and distance to the boarder of d. Corresponding, \f$p_{x,d} = \frac{m_{x,d}}{N_s} gives the relativ
* probability of this matrix cell. \f$ \f$N_v\f$ is the number of voxels. In addition, the marginal
* sums \f$m_{x,\cdot} = m_x = \sum_d m_{x,d} \f$ , representing the sum of all zones with a given intensity, and
* sums \f$m_{\cdot, d} = m_d = \sum_x m_{x,d} \f$ , representing the sum of all zones with a given distance, are used.
* The distance are given as the number of voxels to the border and the voxel intensity is given as the
* bin number of the binned image, starting with 1.
*
* The following features are then defined:
* - <b>Grey Level Distance Zone::Small Distance Emphasis</b>:
* \f[ \textup{Small Distance Emphasis}= \frac{1}{N_s} \sum_d \frac{m_d}{d^2} \f]
* - <b>Grey Level Distance Zone::Large Distance Emphasis</b>:
* \f[ \textup{Large Distance Emphasis}= \frac{1}{N_s} \sum_d d^2 m_d \f]
* - <b>Grey Level Distance Zone::Low Grey Level Emphasis</b>:
* \f[ \textup{Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \frac{m_x}{x^2} \f]
* - <b>Grey Level Distance Zone::High Grey Level Emphasis</b>:
* \f[ \textup{High Grey Level Emphasis}= \frac{1}{N_s} \sum_x x^2 m_x \f]
* - <b>Grey Level Distance Zone::Small Distance Low Grey Level Emphasis</b>:
* \f[ \textup{Small Distance Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{ m_{x,d}}{x^2 d^2}\f]
* - <b>Grey Level Distance Zone::Small Distance High Grey Level Emphasis</b>:
* \f[ \textup{Small Distance High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{x^2 m_{x,d}}{d^2}\f]
* - <b>Grey Level Distance Zone::Large Distance Low Grey Level Emphasis</b>:
* \f[ \textup{Large Distance Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{d^2 m_{x,d}}{x^2}\f]
* - <b>Grey Level Distance Zone::Large Distance High Grey Level Emphasis</b>:
* \f[ \textup{Large Distance High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \x^2 d^2 m_{x,d} \f]
* - <b>Grey Level Distance Zone::Grey Level Non-Uniformity</b>:
* \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_x m_x^2 \f]
* - <b>Grey Level Distance Zone::Grey Level Non-Uniformity Normalized</b>:
* \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_x m_x^2 \f]
* - <b>Grey Level Distance Zone::Zone Distance Non-Uniformity</b>:
* \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_d m_d^2 \f]
* - <b>Grey Level Distance Zone::Zone Distance Non-Uniformity Normalized</b>:
* \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_d m_d^2 \f]
* - <b>Grey Level Distance Zone::Zone Percentage</b>: The ratio of zones to the possible zones:
* \f[ \textup{Zone Percentage}= \frac{N_s}{N_v} \f]
* - <b>Grey Level Distance Zone::Grey Level Mean</b>:
* \f[ \textup{Grey Level Mean}= \mu_x = \sum_x \sum_d x p_{x,d} \f]
* - <b>Grey Level Distance Zone::Grey Level Variance</b>:
* \f[ \textup{Grey Level Variance} = \sum_x \sum_d \left(x - \mu_x \right)^2 p_{x,d} \f]
* - <b>Grey Level Distance Zone::Zone Distance Mean</b>:
* \f[ \textup{Zone Distance Mean}= \mu_d = \sum_x \sum_d d p_{x,d} \f]
* - <b>Grey Level Distance Zone::Zone Distance Variance</b>:
* \f[ \textup{Zone Distance Variance} = \sum_x \sum_d \left(d - \mu_d \right)^2 p_{x,d} \f]
* - <b>Grey Level Distance Zone::Zone Distance Entropy </b>:
* \f[ \textup{Zone Distance Entropy} = - \sum_x \sum_d p_{x,d} \textup{log}_2 ( p_{x,d} ) \f]
* - <b>Grey Level Distance Zone::Grey Level Entropy </b>:
* \f[ \textup{Grey Level Entropy} = - \sum_x \sum_d p_{x,d} \textup{log}_2 ( p_{x,d} ) \f]
*/
public:
mitkClassMacro(GIFGreyLevelDistanceZone, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFGreyLevelDistanceZone();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h
index 480ddb760a..a2d15f05a8 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h
@@ -1,112 +1,112 @@
/*============================================================================
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 mitkGIFGreyLevelRunLength_h
#define mitkGIFGreyLevelRunLength_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the Run Length based features.
*
- * Grey Level Run Length based features are calcualted using the Run-Length-Matrix and were originally
+ * Grey Level Run Length based features are calculated using the Run-Length-Matrix and were originally
* defined by Galloway (1975). The basic idea behind this feature is to measure the length of
* lines with similar intensity in an image. This allows to asses line-based structures in an image.
* For this, the Run-Length-Matrix created that gives the number of lines \f$ m_{x,l} \f$ with the intensity \f$ x \f$ and
* the length of \f$ l \f$ with the given intensity.
*
* The image is quantified prior to the calculation of the features. This reduces the number of
* available intensity values. Instead of using the pure intensity value, the features are
* calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the
* quantification of the image can be controlled using the general binning parameters as defined
* in AbstractGlobalImageFeature.
*
- * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further
+ * By default, the calculation is based on a 26 neighbourhood for 3D and a 8 neighbourhood in 2D. It is further
* possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a
* 3D image is passed. This is controlled by determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* This feature calculator is activated by the option <b>-run-length</b> or <b>-rl</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the same type as the input image. All voxels with a value greater 0.5 are treated as masked.
*
- * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
+ * Several values are defined for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
* \f$N_s \f$ is the number of different lines, \f$ m_{x,\cdot} = \sum_l m{x,l} \f$ is the number of all lines
* with a given intensity value, and likewise \f$ m_{\cdot, l} = \sum_x m{x,l} \f$ is the number of all lines
* with a given length. There are two options how to make this feature orientation-invariant. Either calculating a
* single matrix for all directions and then extracting the features or by calculating a matrix per direction,
* calculating all features and then reporting the mean and standard deviation for each feature. The result
* of the first option is given with the extension "Comb.", the results for the second with "Std." and "Means".
* All three options are always calculated, although we state only one in the following list:
* - <b>Run Length::Short run emphasis Comb.</b>:
* \f[ \textup{Short run emphasis}= \frac{1}{N_s} \sum_l { \frac{m_{\cdot, l}}{l^2} } \f]
* - <b>Run Length::Long run emphasis Comb.</b>:
* \f[ \textup{Long run emphasis}= \frac{1}{N_s} \sum_l { m_{\cdot, l} l^2} \f]
* - <b>Run Length::Low grey level run emphasis Comb.</b>:
* \f[ \textup{Low grey level run emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f]
* - <b>Run Length::High grey level run emphasis Comb.</b>:
* \f[ \textup{High grey level run emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f]
* - <b>Run Length::Short run low grey level emphasis Comb.</b>:
* \f[ \textup{Short run low grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{m_{x,l}}{x^2 l^2} } \f]
* - <b>Run Length::Short run high grey level emphasis Comb.</b>:
* \f[ \textup{Short run high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{x^2 m_{x,s}}{l^2} } \f]
* - <b>Run Length::Long run low grey level emphasis Comb.</b>:
* \f[ \textup{Long run low grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{l^2 m_{x,l}}{x^2} } \f]
* - <b>Run Length::Long run high grey level emphasis Comb.</b>:
* \f[ \textup{Long run high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { x^2 l^2 m_{x,l} } \f]
* - <b>Run Length::Grey level nonuniformity Comb.</b>:
* \f[ \textup{Grey level nonuniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f]
* - <b>Run Length::Grey level nonuniformity normalized Comb.</b>:
* \f[ \textup{Grey level nonuniformity normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f]
* - <b>Run Length::Run length nonuniformity</b>:
* \f[ \textup{Run length nonuniformity}= \frac{1}{N_s} \sum_l m_{\cdot, l}^2 \f]
* - <b>Run Length::Run length nonuniformity normalized</b>:
* \f[ \textup{Run length nonuniformity normalized}= \frac{1}{N_s^2} \sum_l m_{\cdot, l}^2 \f]
* - <b>Run Length::Run percentage</b>: The ratio of realized runs to the theoretical limit of zones:
* \f[ \textup{Run percentage}= \frac{N_s}{N_v} \f]
* - <b>Run Length::Grey level variance</b>:
* \f[ \textup{Grey level variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f]
* - <b>Run Length::Run length variance</b>:
* \f[ \textup{Run length variance} = \frac{1}{N_s} \sum_l (l -mu_l)^2 m_{\cdot, l} \f]
* - <b>Run Length::Run length entropy</b>: This feature would be equivalent with
* the Grey Level Entropy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,s} = \frac{m_{x,s}}{N_s} \f$. :
* \f[ \textup{Run length entropy} = \sum_x \sum_s p_{x,s} \textup{log}_2 \left( p{_x,s} \right) \f]
* - <b>Run Length::Number of runs</b>: The overall number of realized runs:
* \f[ \textup{Number of runs} = \sum m_{x, l} \f]
*/
class MITKCLUTILITIES_EXPORT GIFGreyLevelRunLength : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFGreyLevelRunLength,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFGreyLevelRunLength();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser &parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h
index b9f2a6c9de..72a136d412 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h
@@ -1,112 +1,112 @@
/*============================================================================
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 mitkGIFGreyLevelSizeZone_h
#define mitkGIFGreyLevelSizeZone_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFGreyLevelSizeZone : public AbstractGlobalImageFeature
{
/**
* \brief Calculates the Grey level size zone based features.
*
- * Grey level size zone based features are similar to Grey Level Cooccurence features. But instead
+ * Grey level size zone based features are similar to Grey Level Cooccurrence features. But instead
* of measuring the similarity within a given neighbourhood, the size of areas with the same intensity
* is assessed. For this, a matrix is created that gives the number of areas \f$ m_{x,s} \f$ with the intensity \f$ x \f$ and
* the size of \f$ s \f$. Each area is specified as connected voxels with the given intensity.
*
* The image is quantified prior to the calculation of the features. This reduces the number of
* available intensity values. Instead of using the pure intensity value, the features are
* calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the
* quantification of the image can be controlled using the general binning parameters as defined
* in AbstractGlobalImageFeature.
*
- * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further
+ * By default, the calculation is based on a 26 neighbourhood for 3D and a 8 neighbourhood in 2D. It is further
* possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a
* 3D image is passed. This is controlled by determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* This feature calculator is activated by the option <b>-grey-level-sizezone</b> or <b>-glsz</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
- * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
+ * Several values are defined for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
* \f$N_s \f$ is the number of different zones, \f$ m_{x,\cdot} = \sum_s m{x,s} \f$ is the number of all areas
* with a given intensity value, and likewise \f$ m_{\cdot, s} = \sum_x m{x,s} \f$ is the number of all areas
* with a given size. The features are then defined as:
* - <b>Grey Level Size Zone::Small Zone Emphasis</b>:
* \f[ \textup{Small Zone Emphasis}= \frac{1}{N_s} \sum_s { \frac{m_{\cdot, s}}{s^2} } \f]
* - <b>Grey Level Size Zone::Large Zone Emphasis</b>:
* \f[ \textup{Large Zone Emphasis}= \frac{1}{N_s} \sum_s { m_{\cdot, s} s^2} \f]
* - <b>Grey Level Size Zone::Low Grey Level Zone Emphasis</b>:
* \f[ \textup{Low Grey Level Zone Emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f]
* - <b>Grey Level Size Zone::High Grey Level Zone Emphasis</b>:
* \f[ \textup{High Grey Level Zone Emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f]
* - <b>Grey Level Size Zone::Small Zone Low Grey Level Emphasis</b>:
* \f[ \textup{Small Zone Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{m_{x,s}}{x^2 s^2} } \f]
* - <b>Grey Level Size Zone::Small Zone High Grey Level Emphasis</b>:
* \f[ \textup{Small Zone High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{x^2 m_{x,s}}{s^2} } \f]
* - <b>Grey Level Size Zone::Large Zone Low Grey Level Emphasis</b>:
* \f[ \textup{Large Zone Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{s^2 m_{x,s}}{x^2} } \f]
* - <b>Grey Level Size Zone::Large Zone High Grey Level Emphasis</b>:
* \f[ \textup{Large Zone High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { x^2 s^2 m_{x,s} } \f]
* - <b>Grey Level Size Zone::Grey Level Non-Uniformity</b>:
* \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f]
* - <b>Grey Level Size Zone::Grey Level Non-Uniformity Normalized</b>:
* \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f]
* - <b>Grey Level Size Zone::Zone Size Non-Uniformity</b>:
* \f[ \textup{Zone Size Non-Uniformity}= \frac{1}{N_s} \sum_s m_{\cdot, s}^2 \f]
* - <b>Grey Level Size Zone::Zone Size Non-Uniformity Normalized</b>:
* \f[ \textup{Zone Size Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_s m_{\cdot, s}^2 \f]
* - <b>Grey Level Size Zone::Zone Percentage</b>: The ratio of realized areas to the theoretical limit of zones:
* \f[ \textup{Zone Percentage}= \frac{N_s}{N_v} \f]
* - <b>Grey Level Size Zone::Grey Level Mean</b>:
* \f[ \textup{Grey Level Mean} = \mu_x = \frac{1}{N_s} \sum_x x m_{x, \cdot} \f]
* - <b>Grey Level Size Zone::Grey Level Variance</b>:
* \f[ \textup{Grey Level Variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f]
* - <b>Grey Level Size Zone::Zone Size Mean</b>:
* \f[ \textup{Zone Size Mean} = \mu_s = \frac{1}{N_s} \sum_s s m_{\cdot, s} \f]
* - <b>Grey Level Size Zone::Grey Level Variance</b>:
* \f[ \textup{Grey Level Variance} = \frac{1}{N_s} \sum_s (s -mu_s)^2 m_{\cdot, s} \f]
* - <b>Grey Level Size Zone::Zone Size Entropy</b>: This feature would be equivalent with
* the Grey Level Entropy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,s} = \frac{m_{x,s}}{N_s} \f$. :
* \f[ \textup{Zone Size Entropy} = \sum_x \sum_s p_{x,s} \textup{log}_2 \left( p{_x,s} \right) \f]
*/
public:
mitkClassMacro(GIFGreyLevelSizeZone, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFGreyLevelSizeZone();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h
index e1b8bff712..8dff6b58f6 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h
@@ -1,82 +1,82 @@
/*============================================================================
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 mitkGIFImageDescriptionFeatures_h
#define mitkGIFImageDescriptionFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates simple features that describe the given image / mask
*
* This class can be used to calculated simple image describing features that
* can be used to compare different images.
*
* This feature calculator is activated by the option "<b>-image-diagnostic</b>" or "<b>-id</b>".
* There are no parameters to further define the behavior of this feature calculator.
*
- * The paramters for this image are calculated from two images, a mask and an intenstiy
+ * The parameters for this image are calculated from two images, a mask and an intenstiy
* image. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value larger or equal
* to one are treated as masked. (Standard MITK mask)
*
* The definition of X, Y, and Z axis depends on the image format and does not necessarily
* correlates with axial, coronal, and sagittal.
*
* The features of this calculator are:
* - <b>Diagnostic::Dimension X</b>: The number of voxels in the intensity image along the first image dimension.
* - <b>Diagnostic::Image Dimension Y</b>: The number of voxels in the intensity image along the second image dimension.
* - <b>Diagnostic::Image Dimension Z</b>: The number of voxels in the intensity image along the third image dimension.
* - <b>Diagnostic::Image Spacing X</b>: The spacing of the intensity image in the first image dimension.
* - <b>Diagnostic::Image Spacing Y</b>: The spacing of the intensity image in the second image dimension.
* - <b>Diagnostic::Image Spacing Z</b>: The spacing of the intensity image in the third image dimension.
* - <b>Diagnostic::Image Mean intensity</b>: The mean intensity of the whole image.
* - <b>Diagnostic::Image Minimum intensity</b>: The minimum intensity that occurs in the whole image.
* - <b>Diagnostic::Image Maximum intensity</b>: The maximum intensity that occurs in the whole image.
* - <b>Diagnostic::Mask Dimension X</b>: The number of voxels in the mask image along the first image dimension.
* - <b>Diagnostic::Mask Dimension Y</b>: The number of voxels in the mask image along the second image dimension.
* - <b>Diagnostic::Mask Dimension Z</b>: The number of voxels in the mask image along the third image dimension.
* - <b>Diagnostic::Mask bounding box X</b>: The distance between the maximum and minimum position of a masked voxel along the first axis.
* - <b>Diagnostic::Mask bounding box X</b>: The distance between the maximum and minimum position of a masked voxel along the second axis.
* - <b>Diagnostic::Mask bounding box X</b>: The distance between the maximum and minimum position of a masked voxel along the third axis.
* - <b>Diagnostic::Mask Spacing X</b>: The spacing of the mask image in the first image dimension.
* - <b>Diagnostic::Mask Spacing Y</b>: The spacing of the mask image in the second image dimension.
* - <b>Diagnostic::Mask Spacing Z</b>: The spacing of the mask image in the third image dimension.
* - <b>Diagnostic::Mask Voxel count</b>: The number of voxels that are masked.
* - <b>Diagnostic::Mask Mean intensity</b>: The mean intensity of all voxel that are masked. Also depends on the intensity image.
* - <b>Diagnostic::Mask Minimum intensity</b> The minimum masked intensity in the intensity image.
* - <b>Diagnostic::Mask Maximum intensity</b> The maximum masked intensity in the intensity image.
*/
class MITKCLUTILITIES_EXPORT GIFImageDescriptionFeatures : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFImageDescriptionFeatures,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFImageDescriptionFeatures();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h
index e529d8f04b..49bb94e781 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h
@@ -1,76 +1,76 @@
/*============================================================================
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 mitkGIFIntensityVolumeHistogramFeatures_h
#define mitkGIFIntensityVolumeHistogramFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the Intensity Volume Histogram features
*
* This class can be used to calculate the volume histogram and features that are calculated from
* it. It is based on the intensity-volume histogram (IVH) which describes the relation between the
* grey level index i (and the corresponding intensity \f$x_i\f$) and the volume fraction \f$f\f$ that
* with an intensity that is equal or greater than \f$x_i\f$. This feature is original proposed in
* El Naqa et al. Exploring feature-based approaches in PET images for prediciting cancer treatment outcomes.
* Pattern recognition 2009.
*
* This feature calculator is activated by the option "<b>-intensity-volume-histogram</b>" or "<b>-ivoh</b>".
- * Beside the configuration of the histogram, which follows the describtion given in AbstractGlobalImageFeature
+ * Beside the configuration of the histogram, which follows the description given in AbstractGlobalImageFeature
* there are no additional parameters to configure this feature. Remark that, different from other features,
* the number of bins is 1000 by default and not 256.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of greater than zero
* are treated as masked.
*
* The resulting features are:
- * - <b>Intensity Volume Histogram::Volume fration at 0.10 intensity</b>: The volume fraction with an intensity
+ * - <b>Intensity Volume Histogram::Volume fraction at 0.10 intensity</b>: The volume fraction with an intensity
* of greater or equal to 10% of the maximum intensity.
- * - <b>Intensity Volume Histogram::Volume fration at 0.90 intensity</b>: The volume fraction with an intensity
+ * - <b>Intensity Volume Histogram::Volume fraction at 0.90 intensity</b>: The volume fraction with an intensity
* of greater or equal to 90% of the maximum intensity.
* - <b>Intensity Volume Histogram::Intensity at 0.10 volume</b>: The highest intensity so that at least
* 10% of the masked image area has the same or higher intensity.
* - <b>Intensity Volume Histogram::Intensity at 0.90 volume</b>: The highest intensity so that at least
* 10% of the masked image area has the same or higher intensity.
- * - <b>Intensity Volume Histogram::Difference volume fration at 0.10 and 0.90 intensity</b>: The difference
+ * - <b>Intensity Volume Histogram::Difference volume fraction at 0.10 and 0.90 intensity</b>: The difference
* between the volume fraction at 10% intensity and 90% intensity.
* - <b>Intensity Volume Histogram::Difference intensity at 0.10 and 0.90 volume</b>: The intensity difference
* between the intenstiy of 90% of the volume and 10% volume.
* - <b>Intensity Volume Histogram::Area under IVH curve</b>: If the IVH is interpreted as curve, this value represents
* the area under the curve. It is calculated using the bin indexes rather than the intensity values.
*/
class MITKCLUTILITIES_EXPORT GIFIntensityVolumeHistogramFeatures : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFIntensityVolumeHistogramFeatures, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFIntensityVolumeHistogramFeatures();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h b/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h
index a2f91501a6..48a9661179 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h
@@ -1,72 +1,72 @@
/*============================================================================
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 mitkGIFLocalIntensity_h
#define mitkGIFLocalIntensity_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the local intensity features
*
- * This class can be used to calcualte the local intensity. The local intensity is defined as the
+ * This class can be used to calculate the local intensity. The local intensity is defined as the
* mean intensity in a cube with the volume \f$ 1 \textup{cm}^3\f$ which correspond to a radius
* of approximate 0.62 cm.
*
* This feature calculator is activated by the option <b>-local-intensity</b> or <b>-loci</b>.
*
* The range that is used to calculate the local intensity can be set using the function <b>SetRange</b>.
* To set it with parameters set the option <b>loci::range</b> which expects an float value that is
* interpreted as mm length of the radius. The default value is 6.2.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of greater than zero
* are treated as masked.
*
* The resulting features are:
* - <b>Local Intensity::Local Intensity Peak</b>: This value is defined as the local intensity of the
* voxel with the highest intensity. If there are multiple voxels with the highest intensity, the mean
* of all local intensites of these voxels are reported.
* - <b>Local Intensity::Global Intensity Peak</b>: This value is defined as the highest local intensity
* that occur for all voxels within the mask.
*/
class MITKCLUTILITIES_EXPORT GIFLocalIntensity : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFLocalIntensity, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFLocalIntensity();
itkGetConstMacro(Range, double);
itkSetMacro(Range, double);
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
double m_Range;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h
index e09c99e56c..b0775258d6 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h
@@ -1,90 +1,90 @@
/*============================================================================
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 mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h
#define mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the Neighbourhood Grey Tone Difference Features.
*
* This class can be used to calculate Neighbourhood Grey Tone Difference Features which have been introduced in
* Amadasun and King: Textural features corresponding to textural properties. IEEE Transactions on Systems, Man and Cybernetricy, 1989.
*
- * The Neighbourhood Grey Tone Difference (NGTD) is based on a table and is calcualted using
+ * The Neighbourhood Grey Tone Difference (NGTD) is based on a table and is calculated using
* a defined neighbourhood for each voxel. Within this neighbourhood, the mean intensity of the neighbouring
* voxel is calculated \f$A_i\f$, i.e. the mean intensity of the neighbourhood excluding the center voxel.
* Based on this a table with four columns is calculated. The first column represents the voxel
* value, or in our implementation, the histogram bin index \f$i\f$. The second column represents the
* number of voxel with this intensity value \f$n_i\f$. The proability for each intensity value \f$p_i\f$, which
- * is equal to the bin probability. And the sum of the absolut differences of the intensity of all voxels with this
+ * is equal to the bin probability. And the sum of the absolute differences of the intensity of all voxels with this
* intensity and the mean intensity of their neighbourhood: \f[s_i = \sum \left \| i- A_i \right \| \f].
* Additional \f$ N_v\f$ is the number of voxels, \f$ N_g \f$ the number of bins, and \f$N_{g,p}\f$ the number of
* bins with a non-zero probability.
*
* This feature calculator is activated by the option <b>-neighbourhood-grey-tone-difference</b> or <b>-ngtd</b>.
*
* The range that is used to calculate the local intensity can be set using the function <b>SetRange</b>.
* To set it with parameters set the option <b>ngtd::range</b> which expects an int value n that is
- * interpreted as voxel count. The neighbourhood includes symetrical n voxels additional
+ * interpreted as voxel count. The neighbourhood includes symmetrical n voxels additional
* to the center voxel in each directions. The default value for this parameter is 1.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of greater than zero
* are treated as masked.
*
* For the definition of the features we use the sum, this is always the sum over the bins of the histogram.
* If the denominator of a feature evaluates to zero, the feature is defined as zero.
* The resulting features are:
* - <b>Neighbourhood Grey Tone Difference::Coarsness</b>: The coarsness is defined as :
* \f[ \textup{Coarsness}= \frac{1}{\sum p_i s_i} \f]
* - <b>Neighbourhood Grey Tone Difference::Contrast</b>: The contrast is defined as :
* \f[ \textup{contrast}= \left( \frac{1}{N_{g,p} ( N_{g,p} - 1) } \sum_i \sum_j p_i p_j (i-j)^2 \right) \left( \frac{1}{N_v} \sum s_i \right) \f]
* - <b>Neighbourhood Grey Tone Difference::Busyness</b>: for all bins with a non-zero probability
* \f[ \textup{busyness} = \frac{\sum p_i s_i}{\sum_i \sum_j \left \| i p_i - j p_j \right \| } \f]
* - <b>Neighbourhood Grey Tone Difference::Complexity</b>: for all bins with a non-zero probability
* \f[ \textup{complexity} = \frac{1}{N_v} \sum_i \sum_j \left \| i - j \right \| \frac{p_i s_i + p_j s_j}{p_i + p_j} \f]
* - <b>Neighbourhood Grey Tone Difference::Strength</b>: for all bins with a non-zero probability
* \f[ \textup{strength} = \frac{\sum_i \sum_j (p_i + p_j) (i + j)^2}{\sum_i s_i } \f]
*/
class MITKCLUTILITIES_EXPORT GIFNeighbourhoodGreyToneDifferenceFeatures : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFNeighbourhoodGreyToneDifferenceFeatures, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFNeighbourhoodGreyToneDifferenceFeatures();
itkSetMacro(Range, int);
itkGetConstMacro(Range, int);
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
int m_Range;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h
index 91ffa1adf5..d9f0f60c11 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h
@@ -1,149 +1,149 @@
/*============================================================================
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 mitkGIFNeighbouringGreyLevelDependenceFeatures_h
#define mitkGIFNeighbouringGreyLevelDependenceFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
/**
* \brief Calculates the Neighbouring Grey Level Dependence Features
*
* The Neighbouring Grey Level Dependence Features were proposed by Sun and Wee (1983) and
* capture the coarsness of the image texture. They are rotational invariant.
*
* The features are calculated on a matrix \f$ m \f$. To obtain the matrix, a neighbourhood
* around each feature is calculated and the number of voxels within the neighbourhood that
* are greater than the center voxel plus \f$ \alpha \f$ is counted. This is called the
* number of dependence voxels. The matrix gives the
- * number of voxels with an intesity \f$ x \f$ and \f$ d \f$ dependence neighbourhood voxels.
+ * number of voxels with an intensity \f$ x \f$ and \f$ d \f$ dependence neighbourhood voxels.
*
* The image is quantified prior to the calculation of the features. This reduces the number of
* available intensity values. Instead of using the pure intensity value, the features are
* calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the
* quantification of the image can be controlled using the general binning parameters as defined
* in AbstractGlobalImageFeature.
*
- * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further
+ * By default, the calculation is based on a 26 neighbourhood for 3D and a 8 neighbourhood in 2D. It is further
* possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a
* 3D image is passed. This is controlled by determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
*
* In addition to this, the size of the neighbourhood can be controlled by setting the parameter
* <b>ngld::range</b>. By default it is one. To pass more than one range, separate the ranges with
* a semicolon. E.g. 1;2;3 would calculate the features for the ranges 1, 2, and 3.
*
* This feature calculator is activated by the option <b>-neighbouring-grey-level-dependence</b>
* or <b>-ngld</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* a unsigned short image. All voxels with a value greater 0 are treated as masked.
*
- * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
+ * Several values are defined for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
* \f$N_s \f$ is the number of neighbourhoods, \f$ m_{x,\cdot} = \sum_d m{x,d} \f$ is the number of neighbourhoods
* with a given intensity value, and likewise \f$ m_{\cdot, d} = \sum_x m{x,d} \f$ is the number of neighbourhoods
* with a given number of dependence features:
* - <b>Neighbouring Grey Level Dependence::Low Dependence Emphasis</b>:
* \f[ \textup{Low dependence emphasis}= \frac{1}{N_s} \sum_d { \frac{m_{\cdot, d}}{d^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Dependence Emphasis</b>:
* \f[ \textup{High dependence emphasis}= \frac{1}{N_s} \sum_d { m_{\cdot, d} d^2} \f]
* - <b>Neighbouring Grey Level Dependence::Low Grey Level Count Emphasis</b>:
* \f[ \textup{Low grey level count emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Grey Level Count Emphasis</b>:
* \f[ \textup{High grey level count emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f]
* - <b>Neighbouring Grey Level Dependence::Low Dependence Low Grey Level Emphasis</b>:
* \f[ \textup{Low Dependence Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{m_{x,d}}{x^2 d^2} } \f]
* - <b>Neighbouring Grey Level Dependence::Low Dependence High Grey Level Emphasis</b>:
* \f[ \textup{Low dependence high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{x^2 m_{x,d}}{d^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Dependence Low Grey Level Emphasis</b>:
* \f[ \textup{High Dependence Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{d^2 m_{x,d}}{x^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Dependence High Grey Level Emphasis</b>:
* \f[ \textup{High dependence high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_d { x^2 d^2 m_{x,d} } \f]
* - <b>Neighbouring Grey Level Dependence::Grey level nonuniformity</b>:
* \f[ \textup{Grey level nonuniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Grey level nonuniformity normalized</b>:
* \f[ \textup{Grey level nonuniformity normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Nonuniformity</b>:
* \f[ \textup{Dependence count nonuniformity}= \frac{1}{N_s} \sum_d m_{\cdot, d}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Nonuniformity Normalized</b>:
* \f[ \textup{Dependence count nonuniformity normalized}= \frac{1}{N_s^2} \sum_d m_{\cdot, d}^2 \f]
* - <b>Neighbouring Grey Level Dependence::DEpendence Count Percentage</b> THe number of realized
* neighbourhoods relativ to the theoretical maximum of realized neighbourhoods. This feature is always
* one for this implementation as partial neighbourhoods are still considered.
* - <b>Neighbouring Grey Level Dependence::Grey Level Mean</b>: The mean value of all grey level.
* \f[ \textup{Grey Level Mean} = \mu_x = \frac{1}{N_s} \sum_x x m_{x,\cdot} \f]
* - <b>Neighbouring Grey Level Dependence::Grey Level Variance</b>:
* \f[ \textup{Grey level variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Mean</b>: The mean value of all dependence counts.
* \f[ \textup{Dependence count mean} = \mu_d = \frac{1}{N_s} \sum_d d m_{\cdot,d} \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Variance</b>:
* \f[ \textup{Dependence count variance} = \frac{1}{N_s} \sum_d (d -mu_d)^2 m_{\cdot, d} \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Entropy</b>: This feature would be equivalent with
* the Grey Level Entropy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,d} = \frac{m_{x,d}}{N_s} \f$. :
* \f[ \textup{Dependence count entropy} = \sum_x \sum_d p_{x,d} \textup{log}_2 \left( p_{x,d} \right) \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Energy</b>: This feature would be equivalent with
* the Grey Level Energy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,d} = \frac{m_{x,d}}{N_s} \f$. :
* \f[ \textup{Dependence count energy} = \sum_x \sum_d p_{x,d}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Expected Neighbourhood Size</b>: The expected size of a
* full neighbourhood. It depends on the dimension of the area that is looked at.
* - <b>Neighbouring Grey Level Dependence::Average Neighbourhood Size</b>: The feature calculation
* allows to consider partially masked neighbourhoods. Due to that, some neighbourhoods might be smaller.
* This feature gives not the theoretical neighbourhood size but the average realized neighbourhood sizes.
* - <b>Neighbouring Grey Level Dependence::Average Incomplete Neighbourhood Size</b>: Gives the average
* size of all neighbourhoods that are not complete.
* - <b>Neighbouring Grey Level Dependence::Percentage of complete Neighbourhoods</b>: Gives the percentage
* of all complete neighbourhoods from all realized neighbourhoods.
* - <b>Neighbouring Grey Level Dependence::Percentage of Dependence Neighbour Voxels</b>: Gives the
* percentage of voxels in all neighbourhoods compared to the expected number of voxels.
*/
class MITKCLUTILITIES_EXPORT GIFNeighbouringGreyLevelDependenceFeature : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFNeighbouringGreyLevelDependenceFeature, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFNeighbouringGreyLevelDependenceFeature();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
itkGetConstMacro(Ranges, std::vector<double>);
void SetRanges(std::vector<double> ranges);
void SetRange(double range);
itkGetConstMacro(Alpha, int);
itkSetMacro(Alpha, int);
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
std::vector<double> m_Ranges;
int m_Alpha;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h
index 044a2342bb..4d97c84c33 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h
@@ -1,122 +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.
============================================================================*/
#ifndef mitkGIFVolumetricDensityStatistics_h
#define mitkGIFVolumetricDensityStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates Volumetric Density Features
*
* These features characterize the compactness of the volume and shape by comparing the volumes
* of different volume and shape estimation methods.
*
* This feature calculator is activated by the option <b>-volume-density</b> or <b>-volden</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value equal or greater than 1 are treated as masked.
*
* The volume and surface are compared to the volume \f$ V \f$ and surface \f$ A \f$ that is calculated
* directly from the mask. The following features are then defined:
* - <b>Morphological Density::Volume density axis-aligned bounding box</b>: The axis-aligned bounding
* box is defined as the minimum axis aligned box in 3D space that encloses all masked voxels.
- * It is calculated by using the maximum spacial extension of the mask. Based on the volume of the
+ * It is calculated by using the maximum spatial extension of the mask. Based on the volume of the
* bounding box, \f$ V_{aabb} \f$, the feature is defined as:
* \f[ \textup{Volume density axis-aligned bounding box}= \frac{V}{V_{aabb}} \f]
* - <b>Morphological Density::Surface density axis-aligned bounding box</b>: As for the previous
* feature, the axis-aligned bounding box is compared to the mask, this time using the surface of the
* bounding box \f$ A_{aabb} \f$:
* \f[ \textup{Surface density axis-aligned bounding box}= \frac{A}{A_{aabb}} \f]
* - <b>Morphological Density::Volume density oriented minimum bounding box</b>: A three-dimensional
* bounding box is defined using the box with the minimum volume. We do not use an estimation
* for this feature, which makes the calculation of this feature slow. Based on the volume of the
* bounding box, \f$ V_{ombb} \f$, the feature is defined as:
* \f[ \textup{Volume density oriented minimum bounding box}= \frac{V}{V_{ombb}} \f]
* - <b>Morphological Density::Surface density axis-aligned bounding box</b>: As for the previous
* feature, theminimum oriented bounding box is compared to the mask, this time using the surface of the
* bounding box \f$ A_{ombb} \f$:
* \f[ \textup{Surface density axis-aligned bounding box}= \frac{A}{A_{ombb}} \f]
* - <b>Morphological Density::Volume density approx. enclosing ellipsoid</b>: Using a Principal Component Analysis (PCA)
- * of the spacial coordinates gives the three main axis of the mask. They correspond to the length of
+ * of the spatial coordinates gives the three main axis of the mask. They correspond to the length of
* a eclipse enclosing the mask. The length of the axis of the eclipse are given by the eigenvalues of the
* decomposition: \f$ a = 2 \sqrt{\lambda_1} \f$, \f$ b = 2 \sqrt{\lambda_2} \f$, and \f$ c = 2 \sqrt{\lambda_3} \f$
* with \f$\lambda_x\f$ being the sorted eigenvalues (higher number indicates larger values). The volume
* of the enclosing eclipse can be estimated by \f$ V_{aee} = 4 \pi a b c \f$:
* \f[ \textup{Volume density approx. enclosing ellipsoid}= \frac{V}{V_{aee}} \f]
* - <b>Morphological Density::Surface density approx. enclosing ellipsoid</b>: As for the previous
- * feature, the surface of the enclosing ellipsoid is used. To simplify the calulation of it, an approximation (20 iterations)
+ * feature, the surface of the enclosing ellipsoid is used. To simplify the calculation of it, an approximation (20 iterations)
* for the surface is used (\f$ \alpha = \sqrt{1-\frac{b^2}{a^2}} \f$, \f$ \beta = \sqrt{1-\frac{c^2}{a^2}} \f$):
* \f[ A_{aee} = 2 \pi a b \frac{\alpha^2 + \beta^2}{\alpha \beta} \sum_v^\infty \frac{(a \beta)^v}{1-a v^2} \f]
* \f[ \textup{Surface density approx. enclosing ellipsoid}= \frac{A}{A_{aee}} \f]
* - <b>Morphological Density::Volume density approx. minimum volume enclosing ellipsoid</b>:
* The volume is compared to the volume of the minimum enclosing ellipsoid. While this ellipsoid can be
* found by brute-force calculation, this is quite time-consuming. It is therefore estimated using
* Khachiyan's Algorithm (Khachiyan, Rounding of Polytopes in the Real Number Model of Computation. Mathematics of Operations Research 1996)
* The so-found ellipsoid is described by the lengths \f$a, b, c \f$ of its axis. The volume is then
* defined as \f$ V_{mvee} = 4 \pi a b c \f$ and the feature given by:
* \f[ \textup{Volume density approx. minimum volume enclosing ellipsoid}= \frac{V}{V_{mvee}} \f]
* - <b>Morphological Density::Surface density approx. minimum volume enclosing ellipsoid</b>: As for the previous
- * feature, the surface of the minimum volume enclosing ellipsoid is used. To simplify the calulation of it,
+ * feature, the surface of the minimum volume enclosing ellipsoid is used. To simplify the calculation of it,
* an approximation with 20 iterations instead of infinite iterations is used for the calculation of the
* the surface (\f$ \alpha = \sqrt{1-\frac{b^2}{a^2}} \f$, \f$ \beta = \sqrt{1-\frac{c^2}{a^2}} \f$):
* \f[ A_{mvee} = 2 \pi a b \frac{\alpha^2 + \beta^2}{\alpha \beta} \sum_v^\infty \frac{(a \beta)^v}{1-a v^2} \f]
* \f[ \textup{Surface density approx. minimum volume enclosing ellipsoid}= \frac{A}{A_{mvee}} \f]
* - <b>Morphological Density::Volume density convex hull</b>: The volume of the density
* hull is calculated using a convex mesh and then calculating the volume of this mesh \f$V_{convex} \f$.
* The feature is then calculated using:
* \f[ \textup{Volume density convex hull}= \frac{V}{V_{convex}} \f]
* - <b>Morphological Density::Surface density convex hull</b>: The surface of the density
* hull is calculated using a convex mesh and then calculating the surface of this mesh \f$A_{convex} \f$.
* The feature is then calculated using:
* \f[ \textup{Volume density convex hull}= \frac{A}{A_{convex}} \f]
* - <b>Morphological Density::Volume integrated intensity</b>: Integrated intensity is the
* average intensity times the volume. It is often used in conjunction with PET-images, where
* this feature is also called "total legion glycolysis". It is defined using the volume \f$V \f$, the
* number of masked voxels \f$ N_v \f$ and the intensity of each voxel \f$ x_i \f$:
* \f[ \textup{Volume integrated intensity}= V \frac{1}{N_v} \sum x_i \f]
* - <b>Morphological Density::Volume Moran's I index</b>: Moran's I index is an measure for
- * the spacial autocorrelation. It is defined using the inverse spacial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
+ * the spatial autocorrelation. It is defined using the inverse spatial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
* the number of masked voxels \f$ N_v \f$, the intensity of each voxel \f$ x_i \f$,
* and the mean intensity of all masked voxels \f$ \mu = \frac{1}{N_v} sum x_i \f$:
* \f[ \textup{Volume Moran's I index}= \frac{N_v}{\sum_i \sum_j w_{ij}} \frac{\sum_i \sum_j (x_i - \mu) (x_j -\mu)}{\sum_i (x_i - \mu)^2 } \enspace \enspace {; i \neq j} \f]
* - <b>Morphological Density::Volume Geary's C measure</b>: Geary's C meansure is similar to Moran's I index.
- * However, it is more sensitive to grey level differences and spacial autocorrelation:
- * the spacial autocorrelation. It is defined using the inverse spacial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
+ * However, it is more sensitive to grey level differences and spatial autocorrelation:
+ * the spatial autocorrelation. It is defined using the inverse spatial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
* the number of masked voxels \f$ N_v \f$, the intensity of each voxel \f$ x_i \f$,
* and the mean intensity of all masked voxels \f$ \mu = \frac{1}{N_v} sum x_i \f$:
* \f[ \textup{Volume Geary's C measure}= \frac{N_v - 1}{2 \sum_i \sum_j w_{ij}} \frac{ \sum_i \sum_j w_{ij} (x_i - x_j)^2 }{\sum_i (x_i - \mu)^2 } \enspace \enspace {; i \neq j} \f]
*/
class MITKCLUTILITIES_EXPORT GIFVolumetricDensityStatistics : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFVolumetricDensityStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFVolumetricDensityStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h
index 19f3c0e42d..69b68f3da2 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h
@@ -1,127 +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.
============================================================================*/
#ifndef mitkGIFVolumetricStatistics_h
#define mitkGIFVolumetricStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
- * \brief Calulates simpel shape-related features.
+ * \brief Calculates simpel shape-related features.
*
* This class can be used to calculate simple, shape-related features describing
- * a given segmentation. There are no parameters that can be externaly set.
+ * a given segmentation. There are no parameters that can be externally set.
*
* This feature calculator is activated by the option "<b>-volume</b>" or "<b>-vol</b>"
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value larger or equal
* to one are treated as masked. (Standard MITK mask)
*
* Some of the features are calculated twice using different methods. For voxel-
- * based approaches, the corresponding parameter is calcualted using the voxel,
+ * based approaches, the corresponding parameter is calculated using the voxel,
* for example the volume is then calculated by multiplying the volume of a
* single volume with the number of voxels in the mask. In the second method, the
- * mesh based appraoch, a mesh is created prior to the feature calculation which
+ * mesh based approach, a mesh is created prior to the feature calculation which
* is then done using the features.
*
* Another difference between two features might be the evaluation of invalid
* values within the image. There are two possibilities: By default, only those
* voxels are used with an valid intensity value, i.e. where the value is not
* infinite or NaN. The second possibility is not correcting for these voxels
* and only looking at the mask. Features that use these method are marked as
* "(uncorrected)"
*
* The resulting features are:
* - <b>Volumetric Features:: Voxel Volume</b>: \f$ V_{single\_voxel} \f$ , the volume of an single volume, calculated as the
* multiplication of the voxel spacing in all directions.
- * - <b>Volumetric Features:: Volume (voxel based)</b>: \f$ V_{voxel} \f$, the volume of the masked area. Calulated by
- * multiplying the numer of voxels with the Voxel Volume.
+ * - <b>Volumetric Features:: Volume (voxel based)</b>: \f$ V_{voxel} \f$, the volume of the masked area. Calculated by
+ * multiplying the number of voxels with the Voxel Volume.
* - <b>Volumetric Features:: Volume (mesh based)</b>: \f$ V_{shape} \f$, The volume based on the mesh-representation of
* the mask.
- * - <b>Volumetric Features:: Surface (voxel based)</b>: \f$ A_{voxel} \f$, the surface of the given mask. It is calulated
+ * - <b>Volumetric Features:: Surface (voxel based)</b>: \f$ A_{voxel} \f$, the surface of the given mask. It is calculated
* by summing the surfaces between a masked and an unmasked voxel.
* - <b>Volumetric Features:: Surface (mesh based)</b>: \f$ A_{mesh} \f$, the surface of the given mask calculated using
* the mask representation
* - <b>Volumetric Features:: Surface to volume ration (voxel based)</b>: The ratio between voxel based surface and voxel based
* volume given as: \f[ F_{av\_voxel}=\frac{A_{voxel}}{V_{voxel}} \f]
* - <b>Volumetric Features:: Surface to volume ration (mesh based)</b>: The ratio between voxel based surface and voxel based
* volume given as: \f[ F_{av\_mesh}=\frac{A_{mesh}}{V_{mesh}} \f]
* - <b>Volumetric Features:: Compactness 1 (voxel based)</b>:
* - <b>Volumetric Features:: Compactness 1 (mesh based)</b>:
The compatness is a measure how spheric a shape is given.
* Compactness 1 is defined as:
* \f[ F_{compactness\_1} = \frac{V}{\pi^{1/2} A^{3/2}}\f]
* - <b>Volumetric Features:: Compactness 1 old (voxel based)</b>:
* - <b>Volumetric Features:: Compactness 1 old (mesh based)</b>: Some implementations use a slightly different definition of
* compactness 1. Although this is most likely an error and leads to an non-dimensionless feature,
- * this defition is still calculated as:
+ * this definition is still calculated as:
* \f[ F_{compactness\_1\_old} = \frac{V}{\pi^{1/2} A^{2/3}}\f]
* - <b>Volumetric Features:: Compactness 2 (voxel based)</b>:
* - <b>Volumetric Features:: Compactness 2 (mesh based)</b>: The compatness is a measure how spheric a shape is given.
* Compactness 2 is defined as:
* \f[ F_{compactness\_1} = 36 \pi \frac{V^2}{A^3}\f]
* - <b>Volumetric Features::Sphericity (voxel based)</b>:
* - <b>Volumetric Features::Sphericity (mesh based)</b>: Sphericity is measure of how sphere-like a shape is:
* \f[ F_{sphericity} = \frac{(36 \pi V^2)^{1/3}}{A} \f]
* - <b>Volumetric Features::Asphericity (voxel based)</b>:
* - <b>Volumetric Features::Asphericity (mesh based)</b>: Sphericity is measure of how sphere-like a shape is:
* \f[ F_{asphericity} = \left(\frac{1}{36 \pi }\frac{(A^3}{V^2}\right)^{1/3} - 1 \f]
* - <b>Volumetric Features::Spherical disproportion (voxel based)</b>:
* - <b>Volumetric Features::Spherical disproportion (mesh based)</b>: Sphericity is measure of how sphere-like a shape is:
* \f[ F_{spherical\_disproportion} = \frac{A}{4\pi R^2}= \frac{A}{\left(36\pi V^2\right)^{1/3}} \f]
* - <b>Volumetric Features:: Maximum 3D diameter</b>: This is the largest distance between the centers of two voxels that
* are masked.
* - <b>Volumetric Features::Bounding box volume</b>: The bounding box volume is the volume of the smallest axis-aligned box
* that encapuslates all voxel centres.
* - <b>Volumetric Features::Centre of mass shift</b>:
* - <b>Volumetric Features::Centre of mass shift (uncorrected)</b>: This is the distance between two centres of mass,
* namely the geometric centre and the weighted centre. The geometric centre is the mean position
* of all masked voxels, and the weighted centre is the mean position if the position of each
* voxel is weighted according to its intensity.
* - <b>Volumetric Features::PCA Major Axis length</b>:
* - <b>Volumetric Features::PCA Major Axis length (uncorrected)</b>: A Principal component analysis (PCA) of the masekd voxel
* positions will give the main orientation and elongation of the masked area. The resulting
* eigenvectors of the PCA are sorted so that \f$ \lambda_{major}\geq \lambda_{minor} \geq \lambda_{least}\f$.
* The major axis length is defined as:
* \f[ F_{pca\_major} = 4 \sqrt{\lambda_{major}} \f]
* - <b>Volumetric Features::PCA Minor axis length</b>:
* - <b>Volumetric Features::PCA Minor axis length</b>: The Minor axis length is defined as:
* \f[ F_{pca\_minor} = 4 \sqrt{\lambda_{minor}} \f]
* - <b>Volumetric Features::PCA Least axis length</b>:
* - <b>Volumetric Features::PCA Least axis length</b>: The Minor axis length is defined as:
* \f[ F_{pca\_Least} = 4 \sqrt{\lambda_{Least}} \f]
*/
class MITKCLUTILITIES_EXPORT GIFVolumetricStatistics : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFVolumetricStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFVolumetricStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp
index d806cf63a3..7fe7a3ec46 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.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 <mitkGIFCooccurenceMatrix.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkEnhancedScalarImageToTextureFeaturesFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
// STL
#include <sstream>
struct GIFCooccurenceMatrixConfiguration
{
double range;
unsigned int direction;
double MinimumIntensity;
bool UseMinimumIntensity;
double MaximumIntensity;
bool UseMaximumIntensity;
int Bins;
mitk::FeatureID id;
};
template<typename TPixel, unsigned int VImageDimension>
void
CalculateCoocurenceFeatures(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFCooccurenceMatrix::FeatureListType & featureList, GIFCooccurenceMatrixConfiguration config)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<TPixel, VImageDimension> MaskType;
typedef itk::Statistics::EnhancedScalarImageToTextureFeaturesFilter<ImageType> FilterType;
typedef itk::MinimumMaximumImageCalculator<ImageType> MinMaxComputerType;
typedef typename FilterType::TextureFeaturesFilterType TextureFilterType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer filter = FilterType::New();
typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New();
auto oldOffsets = filter->GetOffsets();
auto oldOffsetsIterator = oldOffsets->Begin();
while(oldOffsetsIterator != oldOffsets->End())
{
bool continueOuterLoop = false;
typename FilterType::OffsetType offset = oldOffsetsIterator->Value();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
offset[i] *= config.range;
if (config.direction == i + 2 && offset[i] != 0)
{
continueOuterLoop = true;
}
}
if (config.direction == 1)
{
offset[0] = 0;
offset[1] = 0;
offset[2] = 1;
newOffset->push_back(offset);
break;
}
oldOffsetsIterator++;
if (continueOuterLoop)
continue;
newOffset->push_back(offset);
}
filter->SetOffsets(newOffset);
// All features are required
typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New();
requestedFeatures->push_back(TextureFilterType::Energy);
requestedFeatures->push_back(TextureFilterType::Entropy);
requestedFeatures->push_back(TextureFilterType::Correlation);
requestedFeatures->push_back(TextureFilterType::InverseDifferenceMoment);
requestedFeatures->push_back(TextureFilterType::Inertia);
requestedFeatures->push_back(TextureFilterType::ClusterShade);
requestedFeatures->push_back(TextureFilterType::ClusterProminence);
requestedFeatures->push_back(TextureFilterType::HaralickCorrelation);
requestedFeatures->push_back(TextureFilterType::Autocorrelation);
requestedFeatures->push_back(TextureFilterType::Contrast);
requestedFeatures->push_back(TextureFilterType::Dissimilarity);
requestedFeatures->push_back(TextureFilterType::MaximumProbability);
requestedFeatures->push_back(TextureFilterType::InverseVariance);
requestedFeatures->push_back(TextureFilterType::Homogeneity1);
requestedFeatures->push_back(TextureFilterType::ClusterTendency);
requestedFeatures->push_back(TextureFilterType::Variance);
requestedFeatures->push_back(TextureFilterType::SumAverage);
requestedFeatures->push_back(TextureFilterType::SumEntropy);
requestedFeatures->push_back(TextureFilterType::SumVariance);
requestedFeatures->push_back(TextureFilterType::DifferenceAverage);
requestedFeatures->push_back(TextureFilterType::DifferenceEntropy);
requestedFeatures->push_back(TextureFilterType::DifferenceVariance);
requestedFeatures->push_back(TextureFilterType::InverseDifferenceMomentNormalized);
requestedFeatures->push_back(TextureFilterType::InverseDifferenceNormalized);
requestedFeatures->push_back(TextureFilterType::InverseDifference);
requestedFeatures->push_back(TextureFilterType::JointAverage);
requestedFeatures->push_back(TextureFilterType::FirstMeasureOfInformationCorrelation);
requestedFeatures->push_back(TextureFilterType::SecondMeasureOfInformationCorrelation);
typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New();
minMaxComputer->SetImage(itkImage);
minMaxComputer->Compute();
filter->SetInput(itkImage);
filter->SetMaskImage(maskImage);
filter->SetRequestedFeatures(requestedFeatures);
double min = minMaxComputer->GetMinimum() - 0.5;
double max = minMaxComputer->GetMaximum() + 0.5;
if (config.UseMinimumIntensity)
min = config.MinimumIntensity;
if (config.UseMaximumIntensity)
max = config.MaximumIntensity;
filter->SetPixelValueMinMax(min,max);
//filter->SetPixelValueMinMax(-1024,3096);
//filter->SetNumberOfBinsPerAxis(5);
filter->Update();
auto featureMeans = filter->GetFeatureMeans ();
auto featureStd = filter->GetFeatureStandardDeviations();
std::ostringstream ss;
ss << config.range;
std::string strRange = ss.str();
for (std::size_t i = 0; i < featureMeans->size(); ++i)
{
switch (i)
{
case TextureFilterType::Energy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Energy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Energy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Entropy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Entropy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Entropy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Correlation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Correlation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Correlation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifferenceMoment :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMoment Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMoment Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Inertia :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Inertia Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Inertia Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::ClusterShade :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterShade Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterShade Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::ClusterProminence :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterProminence Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterProminence Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::HaralickCorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. HaralickCorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. HaralickCorrelation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Autocorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Autocorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Autocorrelation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Contrast :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Contrast Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Contrast Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Dissimilarity :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Dissimilarity Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Dissimilarity Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::MaximumProbability :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. MaximumProbability Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. MaximumProbability Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseVariance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseVariance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseVariance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Homogeneity1 :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Homogeneity1 Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Homogeneity1 Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::ClusterTendency :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterTendency Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterTendency Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Variance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Variance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Variance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SumAverage :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumAverage Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumAverage Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SumEntropy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumEntropy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumEntropy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SumVariance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumVariance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumVariance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::DifferenceAverage :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceAverage Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceAverage Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::DifferenceEntropy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceEntropy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceEntropy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::DifferenceVariance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceVariance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceVariance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifferenceMomentNormalized :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMomentNormalized Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMomentNormalized Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifferenceNormalized :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceNormalized Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceNormalized Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifference :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifference Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifference Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::JointAverage :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. JointAverage Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. JointAverage Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::FirstMeasureOfInformationCorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. FirstMeasureOfInformationCorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. FirstMeasureOfInformationCorrelation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SecondMeasureOfInformationCorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SecondMeasureOfInformationCorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SecondMeasureOfInformationCorrelation Std."), featureStd->ElementAt(i)));
break;
default:
break;
}
}
}
mitk::GIFCooccurenceMatrix::GIFCooccurenceMatrix():
m_Ranges({ 1.0 })
{
SetShortName("deprecated-cooc");
- SetLongName("deprecated-cooccurence");
- SetFeatureClassName("Deprecated Co-occurence Features");
+ SetLongName("deprecated-cooccurrence");
+ SetFeatureClassName("Deprecated Co-occurrence Features");
}
void mitk::GIFCooccurenceMatrix::SetRanges(std::vector<double> ranges)
{
m_Ranges = ranges;
this->Modified();
}
void mitk::GIFCooccurenceMatrix::SetRange(double range)
{
m_Ranges.resize(1);
m_Ranges[0] = range;
this->Modified();
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
InitializeQuantifier(image, mask);
for (const auto& range : m_Ranges)
{
MITK_INFO << "Start calculating coocurence with range " << range << "....";
GIFCooccurenceMatrixConfiguration config;
config.direction = GetDirection();
config.range = range;
config.MinimumIntensity = GetQuantifier()->GetMinimum();
config.MaximumIntensity = GetQuantifier()->GetMaximum();
config.UseMinimumIntensity = GetUseMinimumIntensity();
config.UseMaximumIntensity = GetUseMaximumIntensity();
config.Bins = GetBins();
config.id = this->CreateTemplateFeatureID(std::to_string(range), { {GetOptionPrefix() + "::range", range} });
AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList, config);
MITK_INFO << "Finished calculating coocurence with range " << range << "....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
void mitk::GIFCooccurenceMatrix::AddArguments(mitkCommandLineParser& parser) const
{
std::string name = this->GetOptionPrefix();
- parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features", us::Any());
+ parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurrence matrix", "calculates Co-occurrence based features", us::Any());
parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "Cooc Range", "Define the range that is used (Semicolon-separated)", us::Any());
}
std::string mitk::GIFCooccurenceMatrix::GenerateLegacyFeatureNamePart(const FeatureID& id) const
{
auto result = id.name;
result.insert(7, " (" + id.parameters.at(this->GetOptionPrefix() + "::range").ToString() + ")");
return result;
}
std::string mitk::GIFCooccurenceMatrix::GenerateLegacyFeatureEncoding(const FeatureID& /*id*/) const
{
//deprecated GIFCooccurenceMatrix does not support feature encoding
return "";
}
void mitk::GIFCooccurenceMatrix::ConfigureSettingsByParameters(const ParametersType& parameters)
{
auto name = GetOptionPrefix() + "::range";
if (parameters.count(name))
{
m_Ranges = SplitDouble(parameters.at(name).ToString(), ';');
}
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp
index e64aee3fb4..b2fe3c7d3b 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp
@@ -1,702 +1,702 @@
/*============================================================================
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 <mitkGIFCooccurenceMatrix2.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkEnhancedScalarImageToTextureFeaturesFilter.h>
#include <itkShapedNeighborhoodIterator.h>
#include <itkImageRegionConstIterator.h>
// STL
#include <sstream>
#include <cmath>
namespace mitk
{
struct GIFCooccurenceMatrix2Configuration
{
double range;
unsigned int direction;
double MinimumIntensity;
double MaximumIntensity;
int Bins;
FeatureID id;
};
struct CoocurenceMatrixHolder
{
public:
CoocurenceMatrixHolder(double min, double max, int number);
int IntensityToIndex(double intensity);
double IndexToMinIntensity(int index);
double IndexToMeanIntensity(int index);
double IndexToMaxIntensity(int index);
double m_MinimumRange;
double m_MaximumRange;
double m_Stepsize;
int m_NumberOfBins;
Eigen::MatrixXd m_Matrix;
};
struct CoocurenceMatrixFeatures
{
CoocurenceMatrixFeatures() :
JointMaximum(0),
JointAverage(0),
JointVariance(0),
JointEntropy(0),
RowMaximum(0),
RowAverage(0),
RowVariance(0),
RowEntropy(0),
FirstRowColumnEntropy(0),
SecondRowColumnEntropy(0),
DifferenceAverage(0),
DifferenceVariance(0),
DifferenceEntropy(0),
SumAverage(0),
SumVariance(0),
SumEntropy(0),
AngularSecondMoment(0),
Contrast(0),
Dissimilarity(0),
InverseDifference(0),
InverseDifferenceNormalised(0),
InverseDifferenceMoment(0),
InverseDifferenceMomentNormalised(0),
InverseVariance(0),
Correlation(0),
Autocorrelation(0),
ClusterTendency(0),
ClusterShade(0),
ClusterProminence(0),
FirstMeasureOfInformationCorrelation(0),
SecondMeasureOfInformationCorrelation(0)
{
}
public:
double JointMaximum;
double JointAverage;
double JointVariance;
double JointEntropy;
double RowMaximum;
double RowAverage;
double RowVariance;
double RowEntropy;
double FirstRowColumnEntropy;
double SecondRowColumnEntropy;
double DifferenceAverage;
double DifferenceVariance;
double DifferenceEntropy;
double SumAverage;
double SumVariance;
double SumEntropy;
double AngularSecondMoment;
double Contrast;
double Dissimilarity;
double InverseDifference;
double InverseDifferenceNormalised;
double InverseDifferenceMoment;
double InverseDifferenceMomentNormalised;
double InverseVariance;
double Correlation;
double Autocorrelation;
double ClusterTendency;
double ClusterShade;
double ClusterProminence;
double FirstMeasureOfInformationCorrelation;
double SecondMeasureOfInformationCorrelation;
};
}
static
void MatrixFeaturesTo(const mitk::CoocurenceMatrixFeatures& features,
const std::string& prefix,
const mitk::GIFCooccurenceMatrix2Configuration& config,
mitk::GIFCooccurenceMatrix2::FeatureListType& featureList)
{
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Maximum"), features.JointMaximum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Average"), features.JointAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Variance"), features.JointVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Entropy"), features.JointEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Difference Average"), features.DifferenceAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Difference Variance"), features.DifferenceVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Difference Entropy"), features.DifferenceEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Sum Average"), features.SumAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Sum Variance"), features.SumVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Sum Entropy"), features.SumEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Angular Second Moment"), features.AngularSecondMoment));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Contrast"), features.Contrast));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Dissimilarity"), features.Dissimilarity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference"), features.InverseDifference));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference Normalized"), features.InverseDifferenceNormalised));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference Moment"), features.InverseDifferenceMoment));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference Moment Normalized"), features.InverseDifferenceMomentNormalised));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Variance"), features.InverseVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Correlation"), features.Correlation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Autocorrelation"), features.Autocorrelation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Cluster Tendency"), features.ClusterTendency));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Cluster Shade"), features.ClusterShade));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Cluster Prominence"), features.ClusterProminence));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "First Measure of Information Correlation"), features.FirstMeasureOfInformationCorrelation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Second Measure of Information Correlation"), features.SecondMeasureOfInformationCorrelation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Maximum"), features.RowMaximum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Average"), features.RowAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Variance"), features.RowVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Entropy"), features.RowEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "First Row-Column Entropy"), features.FirstRowColumnEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Second Row-Column Entropy"), features.SecondRowColumnEntropy));
}
static
void CalculateMeanAndStdDevFeatures(std::vector<mitk::CoocurenceMatrixFeatures> featureList,
mitk::CoocurenceMatrixFeatures &meanFeature,
mitk::CoocurenceMatrixFeatures &stdFeature);
static
void NormalizeMatrixFeature(mitk::CoocurenceMatrixFeatures &features,
std::size_t number);
mitk::CoocurenceMatrixHolder::CoocurenceMatrixHolder(double min, double max, int number) :
m_MinimumRange(min),
m_MaximumRange(max),
m_NumberOfBins(number)
{
m_Matrix.resize(number, number);
m_Matrix.fill(0);
m_Stepsize = (max - min) / (number);
}
int mitk::CoocurenceMatrixHolder::IntensityToIndex(double intensity)
{
int index = std::floor((intensity - m_MinimumRange) / m_Stepsize);
return std::max(0, std::min(index, m_NumberOfBins - 1));
}
double mitk::CoocurenceMatrixHolder::IndexToMinIntensity(int index)
{
return m_MinimumRange + index * m_Stepsize;
}
double mitk::CoocurenceMatrixHolder::IndexToMeanIntensity(int index)
{
return m_MinimumRange + (index+0.5) * m_Stepsize;
}
double mitk::CoocurenceMatrixHolder::IndexToMaxIntensity(int index)
{
return m_MinimumRange + (index + 1) * m_Stepsize;
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateCoOcMatrix(const itk::Image<TPixel, VImageDimension>* itkImage,
const itk::Image<unsigned short, VImageDimension>* mask,
itk::Offset<VImageDimension> offset,
int range,
mitk::CoocurenceMatrixHolder &holder)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskImageType;
typedef itk::ShapedNeighborhoodIterator<ImageType> ShapeIterType;
typedef itk::ShapedNeighborhoodIterator<MaskImageType> ShapeMaskIterType;
typedef itk::ImageRegionConstIterator<ImageType> ConstIterType;
typedef itk::ImageRegionConstIterator<MaskImageType> ConstMaskIterType;
itk::Size<VImageDimension> radius;
radius.Fill(range+1);
ShapeIterType imageOffsetIter(radius, itkImage, itkImage->GetLargestPossibleRegion());
ShapeMaskIterType maskOffsetIter(radius, mask, mask->GetLargestPossibleRegion());
imageOffsetIter.ActivateOffset(offset);
maskOffsetIter.ActivateOffset(offset);
ConstIterType imageIter(itkImage, itkImage->GetLargestPossibleRegion());
ConstMaskIterType maskIter(mask, mask->GetLargestPossibleRegion());
// iterator.GetIndex() + ci.GetNeighborhoodOffset()
auto region = mask->GetLargestPossibleRegion();
while (!maskIter.IsAtEnd())
{
auto ciMask = maskOffsetIter.Begin();
auto ciValue = imageOffsetIter.Begin();
if (maskIter.Value() > 0 &&
ciMask.Get() > 0 &&
imageIter.Get() == imageIter.Get() &&
ciValue.Get() == ciValue.Get() &&
region.IsInside(maskOffsetIter.GetIndex() + ciMask.GetNeighborhoodOffset()))
{
int i = holder.IntensityToIndex(imageIter.Get());
int j = holder.IntensityToIndex(ciValue.Get());
holder.m_Matrix(i, j) += 1;
holder.m_Matrix(j, i) += 1;
}
++imageOffsetIter;
++maskOffsetIter;
++imageIter;
++maskIter;
}
}
void CalculateFeatures(
mitk::CoocurenceMatrixHolder &holder,
mitk::CoocurenceMatrixFeatures & results
)
{
auto pijMatrix = holder.m_Matrix;
auto piMatrix = holder.m_Matrix;
auto pjMatrix = holder.m_Matrix;
double Ng = holder.m_NumberOfBins;
int NgSize = holder.m_NumberOfBins;
pijMatrix /= pijMatrix.sum();
piMatrix.rowwise().normalize();
pjMatrix.colwise().normalize();
for (int i = 0; i < holder.m_NumberOfBins; ++i)
for (int j = 0; j < holder.m_NumberOfBins; ++j)
{
if (pijMatrix(i, j) != pijMatrix(i, j))
pijMatrix(i, j) = 0;
if (piMatrix(i, j) != piMatrix(i, j))
piMatrix(i, j) = 0;
if (pjMatrix(i, j) != pjMatrix(i, j))
pjMatrix(i, j) = 0;
}
Eigen::VectorXd piVector = pijMatrix.colwise().sum();
Eigen::VectorXd pjVector = pijMatrix.rowwise().sum();
double sigmai = 0;;
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
double iInt = i + 1;// holder.IndexToMeanIntensity(i);
results.RowAverage += iInt * piVector(i);
if (piVector(i) > 0)
{
results.RowEntropy -= piVector(i) * std::log(piVector(i)) / std::log(2);
}
}
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
double iInt = i + 1; // holder.IndexToMeanIntensity(i);
results.RowVariance += (iInt - results.RowAverage)*(iInt - results.RowAverage) * piVector(i);
}
results.RowMaximum = piVector.maxCoeff();
sigmai = std::sqrt(results.RowVariance);
Eigen::VectorXd pimj(NgSize);
pimj.fill(0);
Eigen::VectorXd pipj(2*NgSize);
pipj.fill(0);
results.JointMaximum += pijMatrix.maxCoeff();
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
for (int j = 0; j < holder.m_NumberOfBins; ++j)
{
//double iInt = holder.IndexToMeanIntensity(i);
//double jInt = holder.IndexToMeanIntensity(j);
double iInt = i + 1;// holder.IndexToMeanIntensity(i);
double jInt = j + 1;// holder.IndexToMeanIntensity(j);
double pij = pijMatrix(i, j);
int deltaK = (i - j)>0?(i-j) : (j-i);
pimj(deltaK) += pij;
pipj(i + j) += pij;
results.JointAverage += iInt * pij;
if (pij > 0)
{
results.JointEntropy -= pij * std::log(pij) / std::log(2);
results.FirstRowColumnEntropy -= pij * std::log(piVector(i)*pjVector(j)) / std::log(2);
}
if (piVector(i) > 0 && pjVector(j) > 0 )
{
results.SecondRowColumnEntropy -= piVector(i)*pjVector(j) * std::log(piVector(i)*pjVector(j)) / std::log(2);
}
results.AngularSecondMoment += pij*pij;
results.Contrast += (iInt - jInt)* (iInt - jInt) * pij;
results.Dissimilarity += std::abs<double>(iInt - jInt) * pij;
results.InverseDifference += pij / (1 + (std::abs<double>(iInt - jInt)));
results.InverseDifferenceNormalised += pij / (1 + (std::abs<double>(iInt - jInt) / Ng));
results.InverseDifferenceMoment += pij / (1 + (iInt - jInt)*(iInt - jInt));
results.InverseDifferenceMomentNormalised += pij / (1 + (iInt - jInt)*(iInt - jInt)/Ng/Ng);
results.Autocorrelation += iInt*jInt * pij;
double cluster = (iInt + jInt - 2 * results.RowAverage);
results.ClusterTendency += cluster*cluster * pij;
results.ClusterShade += cluster*cluster*cluster * pij;
results.ClusterProminence += cluster*cluster*cluster*cluster * pij;
if (iInt != jInt)
{
results.InverseVariance += pij / (iInt - jInt) / (iInt - jInt);
}
}
}
results.Correlation = 1 / sigmai / sigmai * (-results.RowAverage*results.RowAverage+ results.Autocorrelation);
results.FirstMeasureOfInformationCorrelation = (results.JointEntropy - results.FirstRowColumnEntropy) / results.RowEntropy;
if (results.JointEntropy < results.SecondRowColumnEntropy)
{
results.SecondMeasureOfInformationCorrelation = sqrt(1 - exp(-2 * (results.SecondRowColumnEntropy - results.JointEntropy)));
}
else
{
results.SecondMeasureOfInformationCorrelation = 0;
}
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
for (int j = 0; j < holder.m_NumberOfBins; ++j)
{
//double iInt = holder.IndexToMeanIntensity(i);
//double jInt = holder.IndexToMeanIntensity(j);
double iInt = i + 1;
double pij = pijMatrix(i, j);
results.JointVariance += (iInt - results.JointAverage)* (iInt - results.JointAverage)*pij;
}
}
for (int k = 0; k < NgSize; ++k)
{
results.DifferenceAverage += k* pimj(k);
if (pimj(k) > 0)
{
results.DifferenceEntropy -= pimj(k) * log(pimj(k)) / std::log(2);
}
}
for (int k = 0; k < NgSize; ++k)
{
results.DifferenceVariance += (results.DifferenceAverage-k)* (results.DifferenceAverage-k)*pimj(k);
}
for (int k = 0; k <2* NgSize ; ++k)
{
results.SumAverage += (2+k)* pipj(k);
if (pipj(k) > 0)
{
results.SumEntropy -= pipj(k) * log(pipj(k)) / std::log(2);
}
}
for (int k = 0; k < 2*NgSize; ++k)
{
results.SumVariance += (2+k - results.SumAverage)* (2+k - results.SumAverage)*pipj(k);
}
//MITK_INFO << std::endl << holder.m_Matrix;
//MITK_INFO << std::endl << pijMatrix;
//MITK_INFO << std::endl << piMatrix;
//MITK_INFO << std::endl << pjMatrix;
//for (int i = 0; i < holder.m_NumberOfBins; ++i)
//{
// MITK_INFO << "Bin " << i << " Min: " << holder.IndexToMinIntensity(i) << " Max: " << holder.IndexToMaxIntensity(i);
//}
//MITK_INFO << pimj;
//MITK_INFO << pipj;
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateCoocurenceFeatures(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFCooccurenceMatrix2::FeatureListType & featureList, mitk::GIFCooccurenceMatrix2Configuration config)
{
typedef itk::Image<unsigned short, VImageDimension> MaskType;
typedef itk::Neighborhood<TPixel, VImageDimension > NeighborhoodType;
typedef itk::Offset<VImageDimension> OffsetType;
///////////////////////////////////////////////////////////////////////////////////////////////
double rangeMin = config.MinimumIntensity;
double rangeMax = config.MaximumIntensity;
int numberOfBins = config.Bins;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
//Find possible directions
std::vector < itk::Offset<VImageDimension> > offsetVector;
NeighborhoodType hood;
hood.SetRadius(1);
unsigned int centerIndex = hood.GetCenterNeighborhoodIndex();
OffsetType offset;
for (unsigned int d = 0; d < centerIndex; d++)
{
offset = hood.GetOffset(d);
bool useOffset = true;
for (unsigned int i = 0; i < VImageDimension; ++i)
{
offset[i] *= config.range;
if (config.direction == i + 2 && offset[i] != 0)
{
useOffset = false;
}
}
if (useOffset)
{
offsetVector.push_back(offset);
}
}
if (config.direction == 1)
{
offsetVector.clear();
offset[0] = 0;
offset[1] = 0;
offset[2] = 1;
}
std::vector<mitk::CoocurenceMatrixFeatures> resultVector;
mitk::CoocurenceMatrixHolder holderOverall(rangeMin, rangeMax, numberOfBins);
mitk::CoocurenceMatrixFeatures overallFeature;
for (std::size_t i = 0; i < offsetVector.size(); ++i)
{
if (config.direction > 1)
{
if (offsetVector[i][config.direction - 2] != 0)
{
continue;
}
}
offset = offsetVector[i];
mitk::CoocurenceMatrixHolder holder(rangeMin, rangeMax, numberOfBins);
mitk::CoocurenceMatrixFeatures coocResults;
CalculateCoOcMatrix<TPixel, VImageDimension>(itkImage, maskImage, offset, config.range, holder);
holderOverall.m_Matrix += holder.m_Matrix;
CalculateFeatures(holder, coocResults);
resultVector.push_back(coocResults);
}
CalculateFeatures(holderOverall, overallFeature);
//NormalizeMatrixFeature(overallFeature, offsetVector.size());
mitk::CoocurenceMatrixFeatures featureMean;
mitk::CoocurenceMatrixFeatures featureStd;
CalculateMeanAndStdDevFeatures(resultVector, featureMean, featureStd);
std::ostringstream ss;
ss << config.range;
std::string strRange = ss.str();
MatrixFeaturesTo(overallFeature, "Overall ", config, featureList);
MatrixFeaturesTo(featureMean, "Mean ", config, featureList);
MatrixFeaturesTo(featureStd, "Std.Dev. ", config, featureList);
}
static
void CalculateMeanAndStdDevFeatures(std::vector<mitk::CoocurenceMatrixFeatures> featureList,
mitk::CoocurenceMatrixFeatures &meanFeature,
mitk::CoocurenceMatrixFeatures &stdFeature)
{
#define ADDFEATURE(a) \
if ( ! (featureList[i].a == featureList[i].a)) featureList[i].a = 0; \
meanFeature.a += featureList[i].a;stdFeature.a += featureList[i].a*featureList[i].a
#define CALCVARIANCE(a) stdFeature.a =sqrt(stdFeature.a - meanFeature.a*meanFeature.a)
for (std::size_t i = 0; i < featureList.size(); ++i)
{
ADDFEATURE(JointMaximum);
ADDFEATURE(JointAverage);
ADDFEATURE(JointVariance);
ADDFEATURE(JointEntropy);
ADDFEATURE(RowMaximum);
ADDFEATURE(RowAverage);
ADDFEATURE(RowVariance);
ADDFEATURE(RowEntropy);
ADDFEATURE(FirstRowColumnEntropy);
ADDFEATURE(SecondRowColumnEntropy);
ADDFEATURE(DifferenceAverage);
ADDFEATURE(DifferenceVariance);
ADDFEATURE(DifferenceEntropy);
ADDFEATURE(SumAverage);
ADDFEATURE(SumVariance);
ADDFEATURE(SumEntropy);
ADDFEATURE(AngularSecondMoment);
ADDFEATURE(Contrast);
ADDFEATURE(Dissimilarity);
ADDFEATURE(InverseDifference);
ADDFEATURE(InverseDifferenceNormalised);
ADDFEATURE(InverseDifferenceMoment);
ADDFEATURE(InverseDifferenceMomentNormalised);
ADDFEATURE(InverseVariance);
ADDFEATURE(Correlation);
ADDFEATURE(Autocorrelation);
ADDFEATURE(ClusterShade);
ADDFEATURE(ClusterTendency);
ADDFEATURE(ClusterProminence);
ADDFEATURE(FirstMeasureOfInformationCorrelation);
ADDFEATURE(SecondMeasureOfInformationCorrelation);
}
NormalizeMatrixFeature(meanFeature, featureList.size());
NormalizeMatrixFeature(stdFeature, featureList.size());
CALCVARIANCE(JointMaximum);
CALCVARIANCE(JointAverage);
CALCVARIANCE(JointVariance);
CALCVARIANCE(JointEntropy);
CALCVARIANCE(RowMaximum);
CALCVARIANCE(RowAverage);
CALCVARIANCE(RowVariance);
CALCVARIANCE(RowEntropy);
CALCVARIANCE(FirstRowColumnEntropy);
CALCVARIANCE(SecondRowColumnEntropy);
CALCVARIANCE(DifferenceAverage);
CALCVARIANCE(DifferenceVariance);
CALCVARIANCE(DifferenceEntropy);
CALCVARIANCE(SumAverage);
CALCVARIANCE(SumVariance);
CALCVARIANCE(SumEntropy);
CALCVARIANCE(AngularSecondMoment);
CALCVARIANCE(Contrast);
CALCVARIANCE(Dissimilarity);
CALCVARIANCE(InverseDifference);
CALCVARIANCE(InverseDifferenceNormalised);
CALCVARIANCE(InverseDifferenceMoment);
CALCVARIANCE(InverseDifferenceMomentNormalised);
CALCVARIANCE(InverseVariance);
CALCVARIANCE(Correlation);
CALCVARIANCE(Autocorrelation);
CALCVARIANCE(ClusterShade);
CALCVARIANCE(ClusterTendency);
CALCVARIANCE(ClusterProminence);
CALCVARIANCE(FirstMeasureOfInformationCorrelation);
CALCVARIANCE(SecondMeasureOfInformationCorrelation);
#undef ADDFEATURE
#undef CALCVARIANCE
}
static
void NormalizeMatrixFeature(mitk::CoocurenceMatrixFeatures &features,
std::size_t number)
{
features.JointMaximum = features.JointMaximum / number;
features.JointAverage = features.JointAverage / number;
features.JointVariance = features.JointVariance / number;
features.JointEntropy = features.JointEntropy / number;
features.RowMaximum = features.RowMaximum / number;
features.RowAverage = features.RowAverage / number;
features.RowVariance = features.RowVariance / number;
features.RowEntropy = features.RowEntropy / number;
features.FirstRowColumnEntropy = features.FirstRowColumnEntropy / number;
features.SecondRowColumnEntropy = features.SecondRowColumnEntropy / number;
features.DifferenceAverage = features.DifferenceAverage / number;
features.DifferenceVariance = features.DifferenceVariance / number;
features.DifferenceEntropy = features.DifferenceEntropy / number;
features.SumAverage = features.SumAverage / number;
features.SumVariance = features.SumVariance / number;
features.SumEntropy = features.SumEntropy / number;
features.AngularSecondMoment = features.AngularSecondMoment / number;
features.Contrast = features.Contrast / number;
features.Dissimilarity = features.Dissimilarity / number;
features.InverseDifference = features.InverseDifference / number;
features.InverseDifferenceNormalised = features.InverseDifferenceNormalised / number;
features.InverseDifferenceMoment = features.InverseDifferenceMoment / number;
features.InverseDifferenceMomentNormalised = features.InverseDifferenceMomentNormalised / number;
features.InverseVariance = features.InverseVariance / number;
features.Correlation = features.Correlation / number;
features.Autocorrelation = features.Autocorrelation / number;
features.ClusterShade = features.ClusterShade / number;
features.ClusterTendency = features.ClusterTendency / number;
features.ClusterProminence = features.ClusterProminence / number;
features.FirstMeasureOfInformationCorrelation = features.FirstMeasureOfInformationCorrelation / number;
features.SecondMeasureOfInformationCorrelation = features.SecondMeasureOfInformationCorrelation / number;
}
mitk::GIFCooccurenceMatrix2::GIFCooccurenceMatrix2():
m_Ranges({ 1.0 })
{
SetShortName("cooc2");
SetLongName("cooccurence2");
SetFeatureClassName("Co-occurenced Based Features");
}
void mitk::GIFCooccurenceMatrix2::SetRanges(std::vector<double> ranges)
{
m_Ranges = ranges;
this->Modified();
}
void mitk::GIFCooccurenceMatrix2::SetRange(double range)
{
m_Ranges.resize(1);
m_Ranges[0] = range;
this->Modified();
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix2::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
InitializeQuantifier(image, mask);
for (const auto& range: m_Ranges)
{
MITK_INFO << "Start calculating coocurence with range " << range << "....";
GIFCooccurenceMatrix2Configuration config;
config.direction = GetDirection();
config.range = range;
config.MinimumIntensity = GetQuantifier()->GetMinimum();
config.MaximumIntensity = GetQuantifier()->GetMaximum();
config.Bins = GetQuantifier()->GetBins();
config.id = this->CreateTemplateFeatureID(std::to_string(range), { {GetOptionPrefix() + "::range", range} });
AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList, config);
MITK_INFO << "Finished calculating coocurence with range " << range << "....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix2::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
void mitk::GIFCooccurenceMatrix2::AddArguments(mitkCommandLineParser &parser) const
{
this->AddQuantifierArguments(parser);
std::string name = this->GetOptionPrefix();
- parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any());
+ parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurrence matrix", "calculates Co-occurrence based features (new implementation)", us::Any());
parser.addArgument(name+"::range", name+"::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any());
}
std::string mitk::GIFCooccurenceMatrix2::GenerateLegacyFeatureEncoding(const FeatureID& id) const
{
return QuantifierParameterString() + "_Range-" + id.parameters.at(this->GetOptionPrefix()+"::range").ToString();
}
void mitk::GIFCooccurenceMatrix2::ConfigureSettingsByParameters(const ParametersType& parameters)
{
auto name = GetOptionPrefix()+"::range";
if (parameters.count(name))
{
m_Ranges = SplitDouble(parameters.at(name).ToString(), ';');
}
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp
index dd2d2b789f..8913274d43 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.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.
============================================================================*/
#include <mitkGIFFirstOrderHistogramStatistics.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
#include <itkImageRegionConstIteratorWithIndex.h>
// STL
#include <sstream>
#include <limits>
#include <math.h>
namespace mitk
{
struct GIFFirstOrderHistogramStatisticsConfiguration
{
double MinimumIntensity;
double MaximumIntensity;
int Bins;
FeatureID id;
};
}
#define GET_VARIABLE_INDEX(value) \
mv[0] = value; \
histogram->GetIndex(mv, resultingIndex);
template<typename TPixel, unsigned int VImageDimension>
void
CalculateFirstOrderHistogramStatistics(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFFirstOrderHistogramStatistics::FeatureListType & featureList, mitk::GIFFirstOrderHistogramStatisticsConfiguration params)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskType;
typedef itk::LabelStatisticsImageFilter<ImageType, MaskType> FilterType;
typedef typename FilterType::HistogramType HistogramType;
typedef typename HistogramType::IndexType HIndexType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New();
labelStatisticsImageFilter->SetInput(itkImage);
labelStatisticsImageFilter->SetLabelInput(maskImage);
labelStatisticsImageFilter->SetUseHistograms(true);
labelStatisticsImageFilter->SetHistogramParameters(params.Bins, params.MinimumIntensity, params.MaximumIntensity);
labelStatisticsImageFilter->Update();
typename HistogramType::MeasurementVectorType mv(1);
mv[0] = 4.1;
typename HistogramType::IndexType resultingIndex;
auto histogram = labelStatisticsImageFilter->GetHistogram(1);
double meanValue = 0; // labelStatisticsImageFilter->GetMean(1);
GET_VARIABLE_INDEX(meanValue);
double meanIndex = 0; // resultingIndex[0];
double medianValue = labelStatisticsImageFilter->GetMedian(1);
GET_VARIABLE_INDEX(medianValue);
double medianIndex = resultingIndex[0];
double minimumValue = labelStatisticsImageFilter->GetMinimum(1);
GET_VARIABLE_INDEX(minimumValue);
double minimumIndex = resultingIndex[0];
double p10Value = histogram->Quantile(0, 0.10);
GET_VARIABLE_INDEX(p10Value);
double p10Index = resultingIndex[0];
double p25Value = histogram->Quantile(0, 0.25);
GET_VARIABLE_INDEX(p25Value);
double p25Index = resultingIndex[0];
double p75Value = histogram->Quantile(0, 0.75);
GET_VARIABLE_INDEX(p75Value);
double p75Index = resultingIndex[0];
double p90Value = histogram->Quantile(0, 0.90);
GET_VARIABLE_INDEX(p90Value);
double p90Index = resultingIndex[0];
double maximumValue = labelStatisticsImageFilter->GetMaximum(1);
GET_VARIABLE_INDEX(maximumValue);
double maximumIndex = resultingIndex[0];
double Log2 = log(2);
HIndexType index;
HIndexType index2;
index.SetSize(1);
index2.SetSize(1);
double binWidth = histogram->GetBinMax(0, 0) - histogram->GetBinMin(0, 0);
double count = labelStatisticsImageFilter->GetCount(1);
double robustMeanValue = 0;
double robustMeanIndex = 0;
double robustCount = 0;
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
double frequence = histogram->GetFrequency(index);
double probability = frequence / count;
double voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5;
meanValue += probability * voxelValue;
meanIndex += probability * (i);
if ((i >= p10Index) && (i <= p90Index))
{
robustMeanValue += frequence * voxelValue;
robustMeanIndex += frequence * i;
robustCount += frequence;
}
}
robustMeanValue /= robustCount;
robustMeanIndex /= robustCount;
double varianceValue = 0;
double varianceIndex = 0;
double skewnessValue = 0;
double skewnessIndex = 0;
double kurtosisValue = 0;
double kurtosisIndex = 0;
double modeValue = 0;
double modeIndex = 0;
double modeFrequence = 0;
double meanAbsoluteDeviationValue = 0;
double meanAbsoluteDeviationIndex = 0;
double robustMeanAbsoluteDeviationValue = 0;
double robustMeanAbsoluteDeivationIndex = 0;
double medianAbsoluteDeviationValue = 0;
double medianAbsoluteDeviationIndex = 0;
double coefficientOfVariationValue = 0;
double coefficientOfVariationIndex = 0;
double quantileCoefficientOfDispersionValue = 0;
double quantileCoefficientOfDispersionIndex = 0;
double entropyValue = 0;
double entropyIndex = 0;
double uniformityValue = 0;
double uniformityIndex = 0;
double maximumGradientValue = std::numeric_limits<double>::min();
double maximumGradientIndex = 0;
double minimumGradientValue = std::numeric_limits<double>::max();
double minimumGradientIndex = 0;
double gradient = 0;
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
double frequence = histogram->GetFrequency(index);
double probability = frequence / count;
double voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5;
double deltaValue = (voxelValue - meanValue);
double deltaIndex = (i - meanIndex);
varianceValue += probability * deltaValue * deltaValue;
varianceIndex += probability * deltaIndex * deltaIndex;
skewnessValue += probability * deltaValue * deltaValue * deltaValue;
skewnessIndex += probability * deltaIndex * deltaIndex * deltaIndex;
kurtosisValue += probability * deltaValue * deltaValue * deltaValue * deltaValue;
kurtosisIndex += probability * deltaIndex * deltaIndex * deltaIndex * deltaIndex;
if (modeFrequence < frequence)
{
modeFrequence = frequence;
modeValue = voxelValue;
modeIndex = i;
}
meanAbsoluteDeviationValue += probability * std::abs(deltaValue);
meanAbsoluteDeviationIndex += probability * std::abs(deltaIndex);
if ((i >= p10Index) && (i <= p90Index))
{
robustMeanAbsoluteDeviationValue += frequence * std::abs<double>(voxelValue - robustMeanValue);
robustMeanAbsoluteDeivationIndex += frequence * std::abs<double>(i*1.0 - robustMeanIndex*1.0);
}
medianAbsoluteDeviationValue += probability * std::abs(voxelValue - medianValue);
medianAbsoluteDeviationIndex += probability * std::abs(i*1.0 - medianIndex);
if (probability > 0.0000001)
{
entropyValue -= probability * std::log(probability) / Log2;
entropyIndex = entropyValue;
}
uniformityValue += probability*probability;
uniformityIndex = uniformityValue;
if (i == 0)
{
index[0] = 1; index2[0] = 0;
gradient = histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0;
}
else if (i == (int)(histogram->GetSize(0)) - 1)
{
index[0] = i; index2[0] = i - 1;
gradient = histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0;
}
else
{
index[0] = i+1; index2[0] = i - 1;
gradient = (histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0) / 2.0;
}
if (gradient > maximumGradientValue)
{
maximumGradientValue = gradient;
maximumGradientIndex = i + 1;
}
if (gradient < minimumGradientValue)
{
minimumGradientValue = gradient;
minimumGradientIndex = i + 1;
}
}
skewnessValue = skewnessValue / (varianceValue * std::sqrt(varianceValue));
skewnessIndex = skewnessIndex / (varianceIndex * std::sqrt(varianceIndex));
kurtosisValue = kurtosisValue / (varianceValue * varianceValue) - 3; // Excess Kurtosis
kurtosisIndex = kurtosisIndex / (varianceIndex * varianceIndex) - 3; // Excess Kurtosis
coefficientOfVariationValue = std::sqrt(varianceValue) / meanValue;
coefficientOfVariationIndex = std::sqrt(varianceIndex) / (meanIndex+1);
quantileCoefficientOfDispersionValue = (p75Value - p25Value) / (p75Value + p25Value);
quantileCoefficientOfDispersionIndex = (p75Index - p25Index) / (p75Index + 1.0 + p25Index + 1.0);
robustMeanAbsoluteDeviationValue /= robustCount;
robustMeanAbsoluteDeivationIndex /= robustCount;
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Value"), meanValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Variance Value"), varianceValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Skewness Value"), skewnessValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Excess Kurtosis Value"), kurtosisValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Value"), medianValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Value"), minimumValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 10 Value"), p10Value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 90 Value"), p90Value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Value"), maximumValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode Value"), modeValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Interquantile Range Value"), p75Value - p25Value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Range Value"), maximumValue - minimumValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Absolute Deviation Value"), meanAbsoluteDeviationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Absolute Deviation Value"), robustMeanAbsoluteDeviationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Absolute Deviation Value"), medianAbsoluteDeviationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coefficient of Variation Value"), coefficientOfVariationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Quantile coefficient of Dispersion Value"), quantileCoefficientOfDispersionValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Entropy Value"), entropyValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Uniformity Value"), uniformityValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Value"), robustMeanValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Index"), meanIndex + 1 ));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Variance Index"), varianceIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Skewness Index"), skewnessIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Excess Kurtosis Index"), kurtosisIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Index"), medianIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Index"), minimumIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 10 Index"), p10Index + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 90 Index"), p90Index + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Index"), maximumIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode Index"), modeIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Interquantile Range Index"), p75Index - p25Index));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Range Index"), maximumIndex - minimumIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Absolute Deviation Index"), meanAbsoluteDeviationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Absolute Deviation Index"), robustMeanAbsoluteDeivationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Absolute Deviation Index"), medianAbsoluteDeviationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coefficient of Variation Index"), coefficientOfVariationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Quantile coefficient of Dispersion Index"), quantileCoefficientOfDispersionIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Entropy Index"), entropyIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Uniformity Index"), uniformityIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Gradient"), maximumGradientValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Gradient Index"), maximumGradientIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Gradient"), minimumGradientValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Gradient Index"), minimumGradientIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Index"), robustMeanIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Number of Bins"), histogram->GetSize(0)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Bin Size"), binWidth));
}
mitk::GIFFirstOrderHistogramStatistics::GIFFirstOrderHistogramStatistics()
{
SetShortName("foh");
SetLongName("first-order-histogram");
SetFeatureClassName("First Order Histogram");
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderHistogramStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
this->InitializeQuantifier(image, mask);
MITK_INFO << "Start calculating first order histogram features ....";
GIFFirstOrderHistogramStatisticsConfiguration config;
config.MinimumIntensity = GetQuantifier()->GetMinimum();
config.MaximumIntensity = GetQuantifier()->GetMaximum();
config.Bins = GetQuantifier()->GetBins();
config.id = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateFirstOrderHistogramStatistics, mask, featureList, config);
MITK_INFO << "Finished calculating first order histogram features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderHistogramStatistics::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
void mitk::GIFFirstOrderHistogramStatistics::AddArguments(mitkCommandLineParser& parser) const
{
this->AddQuantifierArguments(parser);
std::string name = this->GetOptionPrefix();
- parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any());
+ parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurrence matrix", "calculates Co-occurrence based features (new implementation)", us::Any());
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp
index ea0b3c9a46..5604a20f7a 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp
@@ -1,227 +1,227 @@
/*============================================================================
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 <mitkGIFNeighbourhoodGreyLevelDifference.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
// STL
#include <sstream>
struct GIFNeighbourhoodGreyLevelDifferenceParameterStruct
{
bool m_UseCTRange;
double m_Range;
unsigned int m_Direction;
mitk::FeatureID id;
};
template<typename TPixel, unsigned int VImageDimension>
void
CalculateGrayLevelNeighbourhoodGreyLevelDifferenceFeatures(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFNeighbourhoodGreyLevelDifference::FeatureListType & featureList, GIFNeighbourhoodGreyLevelDifferenceParameterStruct params)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<TPixel, VImageDimension> MaskType;
typedef itk::Statistics::EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter<ImageType> FilterType;
typedef itk::MinimumMaximumImageCalculator<ImageType> MinMaxComputerType;
typedef typename FilterType::NeighbourhoodGreyLevelDifferenceFeaturesFilterType TextureFilterType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer filter = FilterType::New();
typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New();
auto oldOffsets = filter->GetOffsets();
auto oldOffsetsIterator = oldOffsets->Begin();
while (oldOffsetsIterator != oldOffsets->End())
{
bool continueOuterLoop = false;
typename FilterType::OffsetType offset = oldOffsetsIterator->Value();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
if (params.m_Direction == i + 2 && offset[i] != 0)
{
continueOuterLoop = true;
}
}
if (params.m_Direction == 1)
{
offset[0] = 0;
offset[1] = 0;
offset[2] = 1;
newOffset->push_back(offset);
break;
}
oldOffsetsIterator++;
if (continueOuterLoop)
continue;
newOffset->push_back(offset);
}
filter->SetOffsets(newOffset);
// All features are required
typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New();
requestedFeatures->push_back(TextureFilterType::Coarseness);
requestedFeatures->push_back(TextureFilterType::Contrast);
requestedFeatures->push_back(TextureFilterType::Busyness);
requestedFeatures->push_back(TextureFilterType::Complexity);
requestedFeatures->push_back(TextureFilterType::Strength);
typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New();
minMaxComputer->SetImage(itkImage);
minMaxComputer->Compute();
filter->SetInput(itkImage);
filter->SetMaskImage(maskImage);
filter->SetRequestedFeatures(requestedFeatures);
int rangeOfPixels = params.m_Range;
if (rangeOfPixels < 2)
rangeOfPixels = 256;
if (params.m_UseCTRange)
{
filter->SetPixelValueMinMax((TPixel)(-1024.5),(TPixel)(3096.5));
filter->SetNumberOfBinsPerAxis(3096.5+1024.5);
} else
{
filter->SetPixelValueMinMax(minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum());
filter->SetNumberOfBinsPerAxis(rangeOfPixels);
}
filter->SetDistanceValueMinMax(0,rangeOfPixels);
filter->Update();
auto featureMeans = filter->GetFeatureMeans ();
auto featureStd = filter->GetFeatureStandardDeviations();
std::ostringstream ss;
ss << rangeOfPixels;
std::string strRange = ss.str();
for (std::size_t i = 0; i < featureMeans->size(); ++i)
{
switch (i)
{
case TextureFilterType::Coarseness :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coarseness Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Contrast :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Contrast Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Busyness :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Busyness Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Complexity :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Complexity Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Strength :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Strength Means"),featureMeans->ElementAt(i)));
break;
default:
break;
}
}
}
mitk::GIFNeighbourhoodGreyLevelDifference::GIFNeighbourhoodGreyLevelDifference():
m_Ranges({ 1.0 }), m_UseCTRange(false)
{
SetShortName("ngld");
SetLongName("NeighbourhoodGreyLevelDifference");
}
void mitk::GIFNeighbourhoodGreyLevelDifference::SetRanges(std::vector<double> ranges)
{
m_Ranges = ranges;
this->Modified();
}
void mitk::GIFNeighbourhoodGreyLevelDifference::SetRange(double range)
{
m_Ranges.resize(1);
m_Ranges[0] = range;
this->Modified();
}
void mitk::GIFNeighbourhoodGreyLevelDifference::AddArguments(mitkCommandLineParser &parser) const
{
std::string name = GetOptionPrefix();
- parser.addArgument(GetLongName(), name, mitkCommandLineParser::String, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any());
+ parser.addArgument(GetLongName(), name, mitkCommandLineParser::String, "Use Co-occurrence matrix", "calculates Co-occurrence based features (new implementation)", us::Any());
parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any());
parser.addArgument(name + "::direction", name + "::dir", mitkCommandLineParser::Int, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... without dimension 0,1,2... ", us::Any());
parser.addArgument(name + "::useCTRange", name + "::ct", mitkCommandLineParser::Bool, "Use CT range", "If flag is set only value in the CT range will be used for feature computation.", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFNeighbourhoodGreyLevelDifference::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
for (const auto& range : m_Ranges)
{
MITK_INFO << "Start calculating Neighbourhood Grey Level Difference with range " << range << "....";
GIFNeighbourhoodGreyLevelDifferenceParameterStruct params;
params.m_UseCTRange = m_UseCTRange;
params.m_Range = range;
params.m_Direction = GetDirection();
params.id = this->CreateTemplateFeatureID(std::to_string(range), { {GetOptionPrefix() + "::range", range} });
AccessByItk_3(image, CalculateGrayLevelNeighbourhoodGreyLevelDifferenceFeatures, mask, featureList, params);
MITK_INFO << "Finished calculating coocurence with range " << range << "....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFNeighbourhoodGreyLevelDifference::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
std::string mitk::GIFNeighbourhoodGreyLevelDifference::GenerateLegacyFeatureName(const FeatureID& id) const
{
return "NeighbourhoodGreyLevelDifference (" + id.parameters.at(this->GetOptionPrefix() + "::range").ToString() + ") " + id.name;
}
void mitk::GIFNeighbourhoodGreyLevelDifference::ConfigureSettingsByParameters(const ParametersType& parameters)
{
auto prefixname = GetOptionPrefix();
auto name = prefixname + "::range";
if (parameters.count(name))
{
m_Ranges = SplitDouble(parameters.at(name).ToString(), ';');
}
name = prefixname + "::direction";
if (parameters.count(name))
{
int direction = us::any_cast<int>(parameters.at(name));
this->SetDirection(direction);
}
name = prefixname + "::useCTRange";
if (parameters.count(name))
{
bool tmp = us::any_cast<bool>(parameters.at(name));
m_UseCTRange = tmp;
}
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp
index 860265ced9..0f2ee167fe 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp
@@ -1,456 +1,456 @@
/*============================================================================
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 <mitkGIFVolumetricStatistics.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
#include <mitkPixelTypeMultiplex.h>
#include <mitkImagePixelReadAccessor.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkNeighborhoodIterator.h>
// VTK
#include <vtkSmartPointer.h>
#include <vtkImageMarchingCubes.h>
#include <vtkMassProperties.h>
#include <vtkDoubleArray.h>
#include <vtkPCAStatistics.h>
#include <vtkTable.h>
// STL
#include <vnl/vnl_math.h>
template<typename TPixel, unsigned int VImageDimension>
void
CalculateVolumeStatistic(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFVolumetricStatistics::FeatureListType & featureList, const mitk::FeatureID& featureID)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<int, VImageDimension> MaskType;
typedef itk::LabelStatisticsImageFilter<ImageType, MaskType> FilterType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New();
labelStatisticsImageFilter->SetInput( itkImage );
labelStatisticsImageFilter->SetLabelInput(maskImage);
labelStatisticsImageFilter->Update();
double volume = labelStatisticsImageFilter->GetCount(1);
double voxelVolume = 1;
for (int i = 0; i < (int)(VImageDimension); ++i)
{
volume *= itkImage->GetSpacing()[i];
voxelVolume *= itkImage->GetSpacing()[i];
}
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Voxel Volume"), voxelVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Volume (voxel based)"), volume));
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateLargestDiameter(const itk::Image<TPixel, VImageDimension>* mask, const mitk::Image* valueImage, mitk::GIFVolumetricStatistics::FeatureListType & featureList, mitk::FeatureID featureID)
{
typedef itk::Image<double, VImageDimension> ValueImageType;
typename ValueImageType::Pointer itkValueImage = ValueImageType::New();
mitk::CastToItkImage(valueImage, itkValueImage);
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef typename ImageType::PointType PointType;
typename ImageType::SizeType radius;
for (int i=0; i < (int)VImageDimension; ++i)
radius[i] = 1;
itk::ConstNeighborhoodIterator<ImageType> iterator(radius, mask, mask->GetRequestedRegion());
itk::NeighborhoodIterator<ValueImageType> valueIter(radius, itkValueImage, itkValueImage->GetRequestedRegion());
std::vector<PointType> borderPoints;
unsigned int maskDimensionX = mask->GetLargestPossibleRegion().GetSize()[0];
unsigned int maskDimensionY = mask->GetLargestPossibleRegion().GetSize()[1];
unsigned int maskDimensionZ = mask->GetLargestPossibleRegion().GetSize()[2];
double maskVoxelSpacingX = mask->GetSpacing()[0];
double maskVoxelSpacingY = mask->GetSpacing()[1];
double maskVoxelSpacingZ = mask->GetSpacing()[2];
int maskMinimumX = maskDimensionX;
int maskMaximumX = 0;
int maskMinimumY = maskDimensionY;
int maskMaximumY = 0;
int maskMinimumZ = maskDimensionZ;
int maskMaximumZ = 0;
//
// Calculate surface in different directions
//
double surface = 0;
std::vector<double> directionSurface;
for (int i = 0; i < (int)(iterator.Size()); ++i)
{
auto offset = iterator.GetOffset(i);
double deltaS = 1;
int nonZeros = 0;
for (unsigned int j = 0; j < VImageDimension; ++j)
{
if (offset[j] != 0 && nonZeros == 0)
{
for (unsigned int k = 0; k < VImageDimension; ++k)
{
if (k != j)
deltaS *= mask->GetSpacing()[k];
}
nonZeros++;
}
else if (offset[j] != 0)
{
deltaS = 0;
}
}
if (nonZeros < 1)
deltaS = 0;
directionSurface.push_back(deltaS);
}
//
- // Prepare calulation of Centre of mass shift
+ // Prepare calculation of Centre of mass shift
//
PointType normalCenter(0);
PointType normalCenterUncorrected(0);
PointType weightedCenter(0);
PointType currentPoint;
int numberOfPoints = 0;
int numberOfPointsUncorrected = 0;
double sumOfPoints = 0;
while(!iterator.IsAtEnd())
{
if (iterator.GetCenterPixel() == 0)
{
++iterator;
++valueIter;
continue;
}
maskMinimumX = (maskMinimumX > iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMinimumX;
maskMaximumX = (maskMaximumX < iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMaximumX;
maskMinimumY = (maskMinimumY > iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMinimumY;
maskMaximumY = (maskMaximumY < iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMaximumY;
maskMinimumZ = (maskMinimumZ > iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMinimumZ;
maskMaximumZ = (maskMaximumZ < iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMaximumZ;
mask->TransformIndexToPhysicalPoint(iterator.GetIndex(), currentPoint);
normalCenterUncorrected += currentPoint.GetVectorFromOrigin();
++numberOfPointsUncorrected;
double intensityValue = valueIter.GetCenterPixel();
if (intensityValue == intensityValue)
{
normalCenter += currentPoint.GetVectorFromOrigin();
weightedCenter += currentPoint.GetVectorFromOrigin() * intensityValue;
sumOfPoints += intensityValue;
++numberOfPoints;
}
bool border = false;
for (int i = 0; i < (int)(iterator.Size()); ++i)
{
if (iterator.GetPixel(i) == 0 || ( ! iterator.IndexInBounds(i)))
{
border = true;
surface += directionSurface[i];
//break;
}
}
if (border)
{
auto centerIndex = iterator.GetIndex();
PointType centerPoint;
mask->TransformIndexToPhysicalPoint(centerIndex, centerPoint );
borderPoints.push_back(centerPoint);
}
++iterator;
++valueIter;
}
auto normalCenterVector = normalCenter.GetVectorFromOrigin() / numberOfPoints;
auto normalCenterVectorUncorrected = normalCenter.GetVectorFromOrigin() / numberOfPointsUncorrected;
auto weightedCenterVector = weightedCenter.GetVectorFromOrigin() / sumOfPoints;
auto differenceOfCentersUncorrected = (normalCenterVectorUncorrected - weightedCenterVector).GetNorm();
auto differenceOfCenters = (normalCenterVector - weightedCenterVector).GetNorm();
double longestDiameter = 0;
unsigned long numberOfBorderPoints = borderPoints.size();
for (int i = 0; i < (int)numberOfBorderPoints; ++i)
{
auto point = borderPoints[i];
for (int j = i; j < (int)numberOfBorderPoints; ++j)
{
double newDiameter=point.EuclideanDistanceTo(borderPoints[j]);
if (newDiameter > longestDiameter)
longestDiameter = newDiameter;
}
}
double boundingBoxVolume = maskVoxelSpacingX* (maskMaximumX - maskMinimumX) * maskVoxelSpacingY* (maskMaximumY - maskMinimumY) * maskVoxelSpacingZ* (maskMaximumZ - maskMinimumZ);
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Maximum 3D diameter"), longestDiameter));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface (voxel based)"), surface));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Centre of mass shift"), differenceOfCenters));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Centre of mass shift (uncorrected)"), differenceOfCentersUncorrected));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Bounding Box Volume"), boundingBoxVolume));
}
mitk::GIFVolumetricStatistics::GIFVolumetricStatistics()
{
SetLongName("volume");
SetShortName("vol");
SetFeatureClassName("Volumetric Features");
}
void mitk::GIFVolumetricStatistics::AddArguments(mitkCommandLineParser& parser) const
{
std::string name = GetOptionPrefix();
parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Statistic", "calculates volume based features", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFVolumetricStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
MITK_INFO << "Start calculating Volumetric Features:....";
if (image->GetDimension() < 3)
{
return featureList;
}
auto featureID = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateVolumeStatistic, mask, featureList, featureID);
AccessByItk_3(mask, CalculateLargestDiameter, image, featureList, featureID);
vtkSmartPointer<vtkImageMarchingCubes> mesher = vtkSmartPointer<vtkImageMarchingCubes>::New();
vtkSmartPointer<vtkMassProperties> stats = vtkSmartPointer<vtkMassProperties>::New();
auto nonconstVtkData = const_cast<vtkImageData*>(mask->GetVtkImageData());
mesher->SetInputData(nonconstVtkData);
mesher->SetValue(0, 0.5);
stats->SetInputConnection(mesher->GetOutputPort());
stats->Update();
double pi = vnl_math::pi;
double meshVolume = stats->GetVolume();
double meshSurf = stats->GetSurfaceArea();
double pixelVolume = featureList[1].second;
double pixelSurface = featureList[3].second;
MITK_INFO << "Surface: " << pixelSurface << " Volume: " << pixelVolume;
double compactness1 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 2.0 / 3.0));
double compactness1Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 2.0 / 3.0));
//This is the definition used by Aertz. However, due to 2/3 this feature is not demensionless. Use compactness3 instead.
double compactness2 = 36 * pi * pixelVolume * pixelVolume / meshSurf / meshSurf / meshSurf;
double compactness2MeshMesh = 36 * pi * meshVolume * meshVolume / meshSurf / meshSurf / meshSurf;
double compactness2Pixel = 36 * pi * pixelVolume * pixelVolume / pixelSurface / pixelSurface / pixelSurface;
double compactness3 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0));
double compactness3MeshMesh = meshVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0));
double compactness3Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 3.0 / 2.0));
double sphericity = std::pow(pi, 1 / 3.0) * std::pow(6 * pixelVolume, 2.0 / 3.0) / meshSurf;
double sphericityMesh = std::pow(pi, 1 / 3.0) * std::pow(6 * meshVolume, 2.0 / 3.0) / meshSurf;
double sphericityPixel = std::pow(pi, 1 / 3.0) * std::pow(6 * pixelVolume, 2.0 / 3.0) / pixelSurface;
double surfaceToVolume = meshSurf / meshVolume;
double surfaceToVolumePixel = pixelSurface / pixelVolume;
double sphericalDisproportion = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0);
double sphericalDisproportionMesh = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * meshVolume, 2.0 / 3.0);
double sphericalDisproportionPixel = pixelSurface / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0);
double asphericity = std::pow(1.0 / compactness2, (1.0 / 3.0)) - 1;
double asphericityMesh = std::pow(1.0 / compactness2MeshMesh, (1.0 / 3.0)) - 1;
double asphericityPixel = std::pow(1.0 / compactness2Pixel, (1.0 / 3.0)) - 1;
//Calculate center of mass shift
int xx = mask->GetDimensions()[0];
int yy = mask->GetDimensions()[1];
int zz = mask->GetDimensions()[2];
double xd = mask->GetGeometry()->GetSpacing()[0];
double yd = mask->GetGeometry()->GetSpacing()[1];
double zd = mask->GetGeometry()->GetSpacing()[2];
vtkSmartPointer<vtkDoubleArray> dataset1Arr = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset2Arr = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset3Arr = vtkSmartPointer<vtkDoubleArray>::New();
dataset1Arr->SetNumberOfComponents(1);
dataset2Arr->SetNumberOfComponents(1);
dataset3Arr->SetNumberOfComponents(1);
dataset1Arr->SetName("M1");
dataset2Arr->SetName("M2");
dataset3Arr->SetName("M3");
vtkSmartPointer<vtkDoubleArray> dataset1ArrU = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset2ArrU = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset3ArrU = vtkSmartPointer<vtkDoubleArray>::New();
dataset1ArrU->SetNumberOfComponents(1);
dataset2ArrU->SetNumberOfComponents(1);
dataset3ArrU->SetNumberOfComponents(1);
dataset1ArrU->SetName("M1");
dataset2ArrU->SetName("M2");
dataset3ArrU->SetName("M3");
for (int x = 0; x < xx; x++)
{
for (int y = 0; y < yy; y++)
{
for (int z = 0; z < zz; z++)
{
itk::Image<int, 3>::IndexType index;
index[0] = x;
index[1] = y;
index[2] = z;
mitk::ScalarType pxImage;
mitk::ScalarType pxMask;
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
image->GetChannelDescriptor().GetPixelType(),
image,
image->GetVolumeData(),
index,
pxImage,
0);
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
mask->GetChannelDescriptor().GetPixelType(),
mask,
mask->GetVolumeData(),
index,
pxMask,
0);
//Check if voxel is contained in segmentation
if (pxMask > 0)
{
dataset1ArrU->InsertNextValue(x * xd);
dataset2ArrU->InsertNextValue(y * yd);
dataset3ArrU->InsertNextValue(z * zd);
if (pxImage == pxImage)
{
dataset1Arr->InsertNextValue(x * xd);
dataset2Arr->InsertNextValue(y * yd);
dataset3Arr->InsertNextValue(z * zd);
}
}
}
}
}
vtkSmartPointer<vtkTable> datasetTable = vtkSmartPointer<vtkTable>::New();
datasetTable->AddColumn(dataset1Arr);
datasetTable->AddColumn(dataset2Arr);
datasetTable->AddColumn(dataset3Arr);
vtkSmartPointer<vtkTable> datasetTableU = vtkSmartPointer<vtkTable>::New();
datasetTableU->AddColumn(dataset1ArrU);
datasetTableU->AddColumn(dataset2ArrU);
datasetTableU->AddColumn(dataset3ArrU);
vtkSmartPointer<vtkPCAStatistics> pcaStatistics = vtkSmartPointer<vtkPCAStatistics>::New();
pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable);
pcaStatistics->SetColumnStatus("M1", 1);
pcaStatistics->SetColumnStatus("M2", 1);
pcaStatistics->SetColumnStatus("M3", 1);
pcaStatistics->RequestSelectedColumns();
pcaStatistics->SetDeriveOption(true);
pcaStatistics->Update();
vtkSmartPointer<vtkDoubleArray> eigenvalues = vtkSmartPointer<vtkDoubleArray>::New();
pcaStatistics->GetEigenvalues(eigenvalues);
pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTableU);
pcaStatistics->Update();
vtkSmartPointer<vtkDoubleArray> eigenvaluesU = vtkSmartPointer<vtkDoubleArray>::New();
pcaStatistics->GetEigenvalues(eigenvaluesU);
std::vector<double> eigen_val(3);
std::vector<double> eigen_valUC(3);
eigen_val[2] = eigenvalues->GetValue(0);
eigen_val[1] = eigenvalues->GetValue(1);
eigen_val[0] = eigenvalues->GetValue(2);
eigen_valUC[2] = eigenvaluesU->GetValue(0);
eigen_valUC[1] = eigenvaluesU->GetValue(1);
eigen_valUC[0] = eigenvaluesU->GetValue(2);
double major = 4 * sqrt(eigen_val[2]);
double minor = 4 * sqrt(eigen_val[1]);
double least = 4 * sqrt(eigen_val[0]);
double elongation = (major == 0) ? 0 : sqrt(eigen_val[1] / eigen_val[2]);
double flatness = (major == 0) ? 0 : sqrt(eigen_val[0] / eigen_val[2]);
double majorUC = 4 * sqrt(eigen_valUC[2]);
double minorUC = 4 * sqrt(eigen_valUC[1]);
double leastUC = 4 * sqrt(eigen_valUC[0]);
double elongationUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[1] / eigen_valUC[2]);
double flatnessUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[0] / eigen_valUC[2]);
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Volume (mesh based)"), meshVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface (mesh based)"), meshSurf));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface to volume ratio (mesh based)"), surfaceToVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Sphericity (mesh based)"), sphericity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Sphericity (mesh, mesh based)"), sphericityMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Asphericity (mesh based)"), asphericity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Asphericity (mesh, mesh based)"), asphericityMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 (mesh based)"), compactness3));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 old (mesh based)"), compactness1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 2 (mesh based)"), compactness2));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 (mesh, mesh based)"), compactness3MeshMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 2 (mesh, mesh based)"), compactness2MeshMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Spherical disproportion (mesh based)"), sphericalDisproportion));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Spherical disproportion (mesh, mesh based)"), sphericalDisproportionMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface to volume ratio (voxel based)"), surfaceToVolumePixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Sphericity (voxel based)"), sphericityPixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Asphericity (voxel based)"), asphericityPixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 (voxel based)"), compactness3Pixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 old (voxel based)"), compactness1Pixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 2 (voxel based)"), compactness2Pixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Spherical disproportion (voxel based)"), sphericalDisproportionPixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Major axis length"), major));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Minor axis length"), minor));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Least axis length"), least));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Elongation"), elongation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Flatness"), flatness));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Major axis length (uncorrected)"), majorUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Minor axis length (uncorrected)"), minorUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Least axis length (uncorrected)"), leastUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Elongation (uncorrected)"), elongationUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Flatness (uncorrected)"), flatnessUC));
MITK_INFO << "Finished calculating volumetric features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFVolumetricStatistics::CalculateFeatures(const Image* image, const Image* mask, const Image* )
{
return Superclass::CalculateFeatures(image, mask);
}
diff --git a/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp
index 8b12aeca34..cc02fcb505 100644
--- a/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp
+++ b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp
@@ -1,231 +1,231 @@
/*============================================================================
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 <mitkGlobalImageFeaturesParameter.h>
#include <fstream>
#include <itkFileTools.h>
#include <itksys/SystemTools.hxx>
static bool fileExists(const std::string& filename)
{
std::ifstream infile(filename.c_str());
bool isGood = infile.good();
infile.close();
return isGood;
}
void mitk::cl::GlobalImageFeaturesParameter::AddParameter(mitkCommandLineParser &parser)
{
// Required Parameter
parser.addArgument("image", "i", mitkCommandLineParser::Image, "Input Image", "Path to the input image file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("mask", "m", mitkCommandLineParser::Image, "Input Mask", "Path to the mask Image that specifies the area over for the statistic (Values = 1)", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("morph-mask", "morph", mitkCommandLineParser::Image, "Morphological Image Mask", "Path to the mask Image that specifies the area over for the statistic (Values = 1)", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("output", "o", mitkCommandLineParser::File, "Output text file", "Path to output file. The output statistic is appended to this file.", us::Any(), false, false, false, mitkCommandLineParser::Output);
// Optional Parameter
parser.addArgument("xml-output", "x", mitkCommandLineParser::File, "XML result file", "Path where the results should be stored as XML result file. ", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("logfile", "log", mitkCommandLineParser::File, "Text Logfile", "Path to the location of the target log file. ", us::Any(), true, false, false, mitkCommandLineParser::Input);
- parser.addArgument("save-image", "save-image", mitkCommandLineParser::File, "Output Image", "If spezified, the image that is used for the analysis is saved to this location.", us::Any(), true, false, false, mitkCommandLineParser::Output);
- parser.addArgument("save-mask", "save-mask", mitkCommandLineParser::File, "Output Image", "If spezified, the mask that is used for the analysis is saved to this location. ", us::Any(), true, false, false, mitkCommandLineParser::Output);
- parser.addArgument("save-image-screenshots", "save-screenshot", mitkCommandLineParser::File, "Output Image", "If spezified, a screenshot of each slice is saved. Specify an EXISTING folder with prefix (for example ~/demo/ or ~/demo/image-", us::Any(), true, false, false, mitkCommandLineParser::Output);
+ parser.addArgument("save-image", "save-image", mitkCommandLineParser::File, "Output Image", "If specified, the image that is used for the analysis is saved to this location.", us::Any(), true, false, false, mitkCommandLineParser::Output);
+ parser.addArgument("save-mask", "save-mask", mitkCommandLineParser::File, "Output Image", "If specified, the mask that is used for the analysis is saved to this location. ", us::Any(), true, false, false, mitkCommandLineParser::Output);
+ parser.addArgument("save-image-screenshots", "save-screenshot", mitkCommandLineParser::File, "Output Image", "If specified, a screenshot of each slice is saved. Specify an EXISTING folder with prefix (for example ~/demo/ or ~/demo/image-", us::Any(), true, false, false, mitkCommandLineParser::Output);
parser.addArgument("header", "head", mitkCommandLineParser::Bool, "Add Header (Labels) to output", "", us::Any());
parser.addArgument("first-line-header", "fl-head", mitkCommandLineParser::Bool, "Add Header (Labels) to first line of output", "", us::Any());
parser.addArgument("decimal-point", "decimal", mitkCommandLineParser::String, "Decima Point that is used in Conversion", "", us::Any());
parser.addArgument("resample-mask", "rm", mitkCommandLineParser::Bool, "Bool", "Resamples the mask to the resolution of the input image ", us::Any());
parser.addArgument("same-space", "sp", mitkCommandLineParser::Bool, "Bool", "Set the spacing of all images to equal. Otherwise an error will be thrown. ", us::Any());
parser.addArgument("fixed-isotropic", "fi", mitkCommandLineParser::Float, "Float", "Input image resampled to fixed isotropic resolution given in mm. Should be used with resample-mask ", us::Any());
parser.addArgument("minimum-intensity", "minimum", mitkCommandLineParser::Float, "Float", "Minimum intensity. If set, it is overwritten by more specific intensity minima", us::Any());
parser.addArgument("maximum-intensity", "maximum", mitkCommandLineParser::Float, "Float", "Maximum intensity. If set, it is overwritten by more specific intensity maxima", us::Any());
parser.addArgument("bins", "bins", mitkCommandLineParser::Int, "Int", "Number of bins if bins are used. If set, it is overwritten by more specific bin count", us::Any());
parser.addArgument("binsize", "binsize", mitkCommandLineParser::Float, "Int", "Size of bins that is used. If set, it is overwritten by more specific bin count", us::Any());
parser.addArgument("ignore-mask-for-histogram", "ignore-mask", mitkCommandLineParser::Bool, "Bool", "If the whole image is used to calculate the histogram. ", us::Any());
parser.addArgument("encode-parameter-in-name", "encode-parameter", mitkCommandLineParser::Bool, "Bool", "If true, the parameters used for each feature is encoded in its name.", us::Any());
parser.addArgument("pipeline-uid", "p", mitkCommandLineParser::String, "Pipeline UID", "UID that is stored in the XML output and identifies the processing pipeline the app is used in.", us::Any());
parser.addArgument("all-features", "a", mitkCommandLineParser::Bool, "Calculate all features", "If true, all features will be calculated and the feature specific activation will be ignored.", us::Any());
}
void mitk::cl::GlobalImageFeaturesParameter::ParseParameter(std::map<std::string, us::Any> parsedArgs)
{
ParseFileLocations(parsedArgs);
ParseAdditionalOutputs(parsedArgs);
ParseHeaderInformation(parsedArgs);
ParseMaskAdaptation(parsedArgs);
ParseGlobalFeatureParameter(parsedArgs);
}
void mitk::cl::GlobalImageFeaturesParameter::ParseFileLocations(std::map<std::string, us::Any> &parsedArgs)
{
//
// Read input and output file informations
//
imagePath = parsedArgs["image"].ToString();
maskPath = parsedArgs["mask"].ToString();
outputPath = parsedArgs["output"].ToString();
imageFolder = itksys::SystemTools::GetFilenamePath(imagePath);
imageName = itksys::SystemTools::GetFilenameName(imagePath);
maskFolder = itksys::SystemTools::GetFilenamePath(maskPath);
maskName = itksys::SystemTools::GetFilenameName(maskPath);
useMorphMask = false;
if (parsedArgs.count("morph-mask"))
{
useMorphMask = true;
morphPath = parsedArgs["morph-mask"].ToString();
morphName = itksys::SystemTools::GetFilenameName(morphPath);
}
outputXMLPath = "";
if (parsedArgs.count("xml-output"))
{
outputXMLPath = parsedArgs["xml-output"].ToString();
}
}
void mitk::cl::GlobalImageFeaturesParameter::ParseAdditionalOutputs(std::map<std::string, us::Any> &parsedArgs)
{
//
// Read input and output file informations
//
useLogfile = false;
if (parsedArgs.count("logfile"))
{
useLogfile = true;
logfilePath = us::any_cast<std::string>(parsedArgs["logfile"]);
}
writeAnalysisImage = false;
if (parsedArgs.count("save-image"))
{
writeAnalysisImage = true;
anaylsisImagePath = us::any_cast<std::string>(parsedArgs["save-image"]);
}
writeAnalysisMask = false;
if (parsedArgs.count("save-mask"))
{
writeAnalysisMask = true;
analysisMaskPath = us::any_cast<std::string>(parsedArgs["save-mask"]);
}
writePNGScreenshots = false;
if (parsedArgs.count("save-image-screenshots"))
{
writePNGScreenshots = true;
pngScreenshotsPath = us::any_cast<std::string>(parsedArgs["save-image-screenshots"]);
std::string pngScrenshotFolderPath = itksys::SystemTools::GetFilenamePath(pngScreenshotsPath);
if (pngScreenshotsPath.back() == '/' || pngScreenshotsPath.back() == '\\')
{
pngScrenshotFolderPath = pngScreenshotsPath;
}
itk::FileTools::CreateDirectory(pngScrenshotFolderPath.c_str());
}
useDecimalPoint = false;
if (parsedArgs.count("decimal-point"))
{
auto tmpDecimalPoint = us::any_cast<std::string>(parsedArgs["decimal-point"]);
if (tmpDecimalPoint.length() > 0)
{
useDecimalPoint = true;
decimalPoint = tmpDecimalPoint.at(0);
}
}
pipelineUID = "";
if (parsedArgs.count("pipeline-uid"))
{
pipelineUID = us::any_cast<std::string>(parsedArgs["pipeline-uid"]);
}
calculateAllFeatures = parsedArgs.count("all-features");
}
void mitk::cl::GlobalImageFeaturesParameter::ParseHeaderInformation(std::map<std::string, us::Any> &parsedArgs)
{
//
// Check if an header is required or not. Consider also first line header option.
//
useHeader = false;
useHeaderForFirstLineOnly = false;
if (parsedArgs.count("header"))
{
useHeader = us::any_cast<bool>(parsedArgs["header"]);
}
if (parsedArgs.count("first-line-header"))
{
useHeaderForFirstLineOnly = us::any_cast<bool>(parsedArgs["first-line-header"]);
}
if (useHeaderForFirstLineOnly)
{
useHeader = !fileExists(outputPath);
}
}
void mitk::cl::GlobalImageFeaturesParameter::ParseMaskAdaptation(std::map<std::string, us::Any> &parsedArgs)
{
//
// Parse parameters that control how the input mask is adapted to the input image
//
resampleMask = false;
ensureSameSpace = false;
resampleToFixIsotropic = false;
resampleResolution = 1.0;
if (parsedArgs.count("resample-mask"))
{
resampleMask = us::any_cast<bool>(parsedArgs["resample-mask"]);
}
if (parsedArgs.count("same-space"))
{
ensureSameSpace = us::any_cast<bool>(parsedArgs["same-space"]);
}
if (parsedArgs.count("fixed-isotropic"))
{
resampleToFixIsotropic = true;
resampleResolution = us::any_cast<float>(parsedArgs["fixed-isotropic"]);
}
}
void mitk::cl::GlobalImageFeaturesParameter::ParseGlobalFeatureParameter(std::map<std::string, us::Any> &parsedArgs)
{
//
// Parse parameters that control how the input mask is adapted to the input image
//
defineGlobalMinimumIntensity = false;
defineGlobalMaximumIntensity = false;
defineGlobalNumberOfBins = false;
encodeParameter = false;
if (parsedArgs.count("minimum-intensity"))
{
defineGlobalMinimumIntensity = true;
globalMinimumIntensity = us::any_cast<float>(parsedArgs["minimum-intensity"]);
}
if (parsedArgs.count("maximum-intensity"))
{
defineGlobalMaximumIntensity = true;
globalMaximumIntensity = us::any_cast<float>(parsedArgs["maximum-intensity"]);
}
if (parsedArgs.count("bins"))
{
defineGlobalNumberOfBins = true;
globalNumberOfBins = us::any_cast<int>(parsedArgs["bins"]);
}
if (parsedArgs.count("encode-parameter-in-name"))
{
encodeParameter = true;
}
}
diff --git a/Modules/CommandLine/include/mitkCommandLineParser.h b/Modules/CommandLine/include/mitkCommandLineParser.h
index f91fe987d1..a751acbb20 100644
--- a/Modules/CommandLine/include/mitkCommandLineParser.h
+++ b/Modules/CommandLine/include/mitkCommandLineParser.h
@@ -1,372 +1,372 @@
/*============================================================================
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 mitkCommandLineParser_h
#define mitkCommandLineParser_h
#include <map>
#include <usAny.h>
#include <MitkCommandLineExports.h>
#include <mitkVersion.h>
/**
*
* The MITK command line parser, based on the CTK command line parser.
*
* Use this class to add information about the command line arguments
* your program understands and to easily parse them from a given list
* of strings.
*
* This parser provides the following features:
*
* <ul>
* <li>Add arguments by supplying a long name and/or a short name.
* Arguments are validated using a regular expression. They can have
* a default value and a help string.</li>
* <li>Deprecated arguments.</li>
* <li>Custom regular expressions for argument validation.</li>
* <li>Set different argument name prefixes for native platform look and feel.</li>
* <li>Create a help text for the command line arguments with support for
* grouping arguments.</li>
* </ul>
*
* The main difference between the MITK command line parser and the CTK command line
* parser is that the former does not depend on Qt. Apart from that an image type was
* added and XML output improved for automatic GUI generation.
*
* std::out is used for output to keep dependencies to a minimum.
*/
class MITKCOMMANDLINE_EXPORT mitkCommandLineParser
{
public:
enum Type
{
String = 0,
Bool = 1,
StringList = 2,
Int = 3,
Float = 4,
Directory = 5,
File = 6,
Image = 7
};
enum Channel
{
None = 0,
Input = 1,
Output = 2
};
typedef std::vector<std::string> StringContainerType;
mitkCommandLineParser();
~mitkCommandLineParser();
/**
* Parse a given list of command line arguments.
*
* This method parses a list of string elements considering the known arguments
* added by calls to <code>addArgument()</code>. If any one of the argument
* values does not match the corresponding regular expression,
* <code>ok</code> is set to false and an empty map object is returned.
*
* The keys in the returned map object correspond to the long argument string,
* if it is not empty. Otherwise, the short argument string is used as key. The
* us::Any values can safely be converted to the type specified in the
* <code>addArgument()</code> method call.
*
* @param arguments A StringContainerType containing command line arguments.
* @param ok A pointer to a boolean variable. Will be set to <code>true</code>
* if all regular expressions matched, <code>false</code> otherwise.
* @return A map object mapping the long argument (if empty, the short one)
* to a us::Any containing the value.
*/
std::map<std::string, us::Any> parseArguments(const StringContainerType &arguments, bool *ok = nullptr);
/**
* Convenient method allowing to parse a given list of command line arguments.
* @see parseArguments(const StringContainerType &, bool*)
*/
std::map<std::string, us::Any> parseArguments(int argc, char **argv, bool *ok = nullptr);
/**
* Returns a detailed error description if a call to <code>parseArguments()</code>
* failed.
*
- * @return The error description, empty if no error occured.
+ * @return The error description, empty if no error occurred.
* @see parseArguments(const StringContainerType&, bool*)
*/
std::string errorString() const;
/**
* This method returns all unparsed arguments, i.e. all arguments
* for which no long or short name has been registered via a call
* to <code>addArgument()</code>.
*
* @see addArgument()
*
* @return A list containing unparsed arguments.
*/
const StringContainerType &unparsedArguments() const;
/**
* Checks if the given argument has been added via a call
* to <code>addArgument()</code>.
*
* @see addArgument()
*
* @param argument The argument to be checked.
* @return <code>true</code> if the argument was added, <code>false</code>
* otherwise.
*/
bool argumentAdded(const std::string &argument) const;
/**
* Checks if the given argument has been parsed successfully by a previous
* call to <code>parseArguments()</code>.
*
* @param argument The argument to be checked.
* @return <code>true</code> if the argument was parsed, <code>false</code>
* otherwise.
*/
bool argumentParsed(const std::string &argument) const;
/**
* Adds a command line argument. An argument can have a long name
* (like --long-argument-name), a short name (like -l), or both. The type
* of the argument can be specified by using the <code>type</code> parameter.
* The following types are supported:
*
* <table>
* <tr><td><b>Type</b></td><td><b># of parameters</b></td><td><b>Default regular expr</b></td>
* <td><b>Example</b></td></tr>
* <tr><td>us::Any::String</td><td>1</td><td>.*</td><td>--test-string StringParameter</td></tr>
* <tr><td>us::Any::Bool</td><td>0</td><td>does not apply</td><td>--enable-something</td></tr>
* <tr><td>us::Any::StringList</td><td>-1</td><td>.*</td><td>--test-list string1 string2</td></tr>
* <tr><td>us::Any::Int</td><td>1</td><td>-?[0-9]+</td><td>--test-int -5</td></tr>
* </table>
*
* The regular expressions are used to validate the parameters of command line
* arguments. You can restrict the valid set of parameters by calling
* <code>setExactMatchRegularExpression()</code> for your argument.
*
* Optionally, a help string and a default value can be provided for the argument. If
* the us::Any type of the default value does not match <code>type</code>, an
* exception is thrown. Arguments with default values are always returned by
* <code>parseArguments()</code>.
*
* You can also declare an argument deprecated, by setting <code>deprecated</code>
* to <code>true</code>. Alternatively you can add a deprecated argument by calling
* <code>addDeprecatedArgument()</code>.
*
* If the long or short argument has already been added, or if both are empty strings,
* the method call has no effect.
*
* @param longarg The long argument name.
* @param shortarg The short argument name.
* @param type The argument type (see the list above for supported types).
* @param argLabel The label of this argument, when auto generated interface is used.
* @param argHelp A help string describing the argument.
* @param defaultValue A default value for the argument.
* @param optional
* @param ignoreRest All arguments after the current one will be ignored.
* @param deprecated Declares the argument deprecated.
* @param channel
*
* @see setExactMatchRegularExpression()
* @see addDeprecatedArgument()
* @throws std::logic_error If the us::Any type of <code>defaultValue</code>
* does not match <code>type</code>, a <code>std::logic_error</code> is thrown.
*/
void addArgument(const std::string &longarg,
const std::string &shortarg,
Type type,
const std::string &argLabel,
const std::string &argHelp = std::string(),
const us::Any &defaultValue = us::Any(),
bool optional = true,
bool ignoreRest = false,
bool deprecated = false,
mitkCommandLineParser::Channel channel = mitkCommandLineParser::Channel::None);
/**
* Adds a deprecated command line argument. If a deprecated argument is provided
* on the command line, <code>argHelp</code> is displayed in the console and
* processing continues with the next argument.
*
* Deprecated arguments are grouped separately at the end of the help text
* returned by <code>helpText()</code>.
*
* @param longarg The long argument name.
* @param shortarg The short argument name.
* @param argLabel
* @param argHelp A help string describing alternatives to the deprecated argument.
*/
void addDeprecatedArgument(const std::string &longarg,
const std::string &shortarg,
const std::string &argLabel,
const std::string &argHelp);
/**
* Returns the vector of current Command line Parameter
*
*/
std::vector < std::map<std::string, us::Any> > getArgumentList();
/**
* Sets a custom regular expression for validating argument parameters. The method
* <code>errorString()</code> can be used the get the last error description.
*
* @param argument The previously added long or short argument name.
* @param expression A regular expression which the arugment parameters must match.
* @param exactMatchFailedMessage An error message explaining why the parameter did
* not match.
*
* @return <code>true</code> if the argument was found and the regular expression was set,
* <code>false</code> otherwise.
*
* @see errorString()
*/
bool setExactMatchRegularExpression(const std::string &argument,
const std::string &expression,
const std::string &exactMatchFailedMessage);
/**
* The field width for the argument names without the help text.
*
* @return The argument names field width in the help text.
*/
std::string::size_type fieldWidth() const;
/**
* Creates a help text containing properly formatted argument names and help strings
* provided by calls to <code>addArgument()</code>. The arguments can be grouped by
* using <code>beginGroup()</code> and <code>endGroup()</code>.
*
* @return The formatted help text.
*/
std::string helpText() const;
/**
* Sets the argument prefix for long and short argument names. This can be used
* to create native command line arguments without changing the calls to
* <code>addArgument()</code>. For example on Unix-based systems, long argument
* names start with "--" and short names with "-", while on Windows argument names
* always start with "/".
*
* Note that all methods in mitkCommandLineParser which take an argument name
* expect the name as it was supplied to <code>addArgument</code>.
*
* Example usage:
*
* \code
* ctkCommandLineParser parser;
* parser.setArgumentPrefix("--", "-");
* parser.addArgument("long-argument", "l", us::Any::String);
* StringContainerType args;
* args << "program name" << "--long-argument Hi";
* parser.parseArguments(args);
* \endcode
*
* @param longPrefix The prefix for long argument names.
* @param shortPrefix The prefix for short argument names.
*/
void setArgumentPrefix(const std::string &longPrefix, const std::string &shortPrefix);
/**
* Begins a new group for documenting arguments. All newly added arguments via
* <code>addArgument()</code> will be put in the new group. You can close the
* current group by calling <code>endGroup()</code> or be opening a new group.
*
* Note that groups cannot be nested and all arguments which do not belong to
* a group will be listed at the top of the text created by <code>helpText()</code>.
*
* @param description The description of the group
*/
void beginGroup(const std::string &description);
/**
* Ends the current group.
*
* @see beginGroup(const std::string&)
*/
void endGroup();
/**
* Can be used to teach the parser to stop parsing the arguments and return False when
* an unknown argument is encountered. By default <code>StrictMode</code> is disabled.
*
* @see parseArguments(const StringContainerType &, bool*)
*/
void setStrictModeEnabled(bool strictMode);
/**
* Is used to generate an XML output for any commandline program.
*/
void generateXmlOutput();
/**
* Is used to set the title of the auto generated interface.
*
* @param title The title of the app.
*/
void setTitle(std::string title);
/**
* Is used to set the contributor for the help view in the auto generated interface.
*
* @param contributor Contributor of the app.
*/
void setContributor(std::string contributor);
/**
* Is used to categorize the apps in the commandline module.
*
* @param category The category of the app.
*/
void setCategory(std::string category);
/**
* Is used as the help text in the auto generated interface.
*
* @param description A short description for the app.
*/
void setDescription(std::string description);
/**
* Is used to group several Parameters in one groupbox in the auto generated interface.
* Default name is "Parameters", with the tooltip: "Groupbox containing parameters."
*
* To change the group of several arguments, call this method before the arguments are added.
*
* @param name The name of the groupbox.
* @param tooltip The tooltip of the groupbox.
*/
void changeParameterGroup(std::string name, std::string tooltip);
protected:
class ctkInternal;
ctkInternal *Internal;
std::string Title;
std::string Contributor;
std::string Category;
std::string Description;
std::string ParameterGroupName;
std::string ParameterGroupDescription;
};
#endif
diff --git a/Modules/CommandLine/src/mitkCommandLineParser.cpp b/Modules/CommandLine/src/mitkCommandLineParser.cpp
index cf60f88d99..72215d1221 100644
--- a/Modules/CommandLine/src/mitkCommandLineParser.cpp
+++ b/Modules/CommandLine/src/mitkCommandLineParser.cpp
@@ -1,1056 +1,1056 @@
/*============================================================================
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.
============================================================================*/
/*=========================================================================
Library: CTK
Copyright (c) Kitware Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0.txt
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=========================================================================*/
// STL includes
#include <iostream>
#include <stdexcept>
// MITK includes
#include "mitkCommandLineParser.h"
using namespace std;
namespace
{
// --------------------------------------------------------------------------
class CommandLineParserArgumentDescription
{
public:
CommandLineParserArgumentDescription(const string &longArg,
const string &longArgPrefix,
const string &shortArg,
const string &shortArgPrefix,
mitkCommandLineParser::Type type,
const string &argHelp,
const string &argLabel,
const us::Any &defaultValue,
bool ignoreRest,
bool deprecated,
bool optional,
mitkCommandLineParser::Channel channel,
string &argGroup,
string &groupDescription)
: LongArg(longArg),
LongArgPrefix(longArgPrefix),
ShortArg(shortArg),
ShortArgPrefix(shortArgPrefix),
ArgHelp(argHelp),
ArgLabel(argLabel),
ArgGroup(argGroup),
ArgGroupDescription(groupDescription),
IgnoreRest(ignoreRest),
NumberOfParametersToProcess(0),
Deprecated(deprecated),
Optional(optional),
Channel(channel),
DefaultValue(defaultValue),
Value(type),
ValueType(type)
{
Value = defaultValue;
switch (type)
{
case mitkCommandLineParser::String:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Bool:
{
NumberOfParametersToProcess = 0;
}
break;
case mitkCommandLineParser::StringList:
{
NumberOfParametersToProcess = -1;
}
break;
case mitkCommandLineParser::Int:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Float:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Directory:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::File:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Image:
{
NumberOfParametersToProcess = 1;
}
break;
default:
std::cout << "Type not supported: " << static_cast<int>(type);
}
}
~CommandLineParserArgumentDescription() {}
bool addParameter(const string &value);
string helpText();
string LongArg;
string LongArgPrefix;
string ShortArg;
string ShortArgPrefix;
string ArgHelp;
string ArgLabel;
string ArgGroup;
string ArgGroupDescription;
bool IgnoreRest;
int NumberOfParametersToProcess;
bool Deprecated;
bool Optional;
mitkCommandLineParser::Channel Channel;
us::Any DefaultValue;
us::Any Value;
mitkCommandLineParser::Type ValueType;
};
// --------------------------------------------------------------------------
bool CommandLineParserArgumentDescription::addParameter(const string &value)
{
switch (ValueType)
{
case mitkCommandLineParser::String:
{
Value = value;
}
break;
case mitkCommandLineParser::Bool:
{
if (value.compare("true") == 0)
Value = true;
else
Value = false;
}
break;
case mitkCommandLineParser::StringList:
{
try
{
mitkCommandLineParser::StringContainerType list =
us::any_cast<mitkCommandLineParser::StringContainerType>(Value);
list.push_back(value);
Value = list;
}
catch (...)
{
mitkCommandLineParser::StringContainerType list;
list.push_back(value);
Value = list;
}
}
break;
case mitkCommandLineParser::Int:
{
stringstream ss(value);
int i;
ss >> i;
Value = i;
}
break;
case mitkCommandLineParser::Float:
{
stringstream ss(value);
float f;
ss >> f;
Value = f;
}
break;
case mitkCommandLineParser::Directory:
case mitkCommandLineParser::Image:
case mitkCommandLineParser::File:
{
Value = value;
}
break;
default:
return false;
}
return true;
}
// --------------------------------------------------------------------------
string CommandLineParserArgumentDescription::helpText()
{
string text;
string shortAndLongArg;
if (!this->ShortArg.empty())
{
shortAndLongArg = " ";
shortAndLongArg += this->ShortArgPrefix;
shortAndLongArg += this->ShortArg;
}
if (!this->LongArg.empty())
{
if (this->ShortArg.empty())
shortAndLongArg.append(" ");
else
shortAndLongArg.append(", ");
shortAndLongArg += this->LongArgPrefix;
shortAndLongArg += this->LongArg;
}
text = text + shortAndLongArg + ", " + this->ArgHelp;
if (this->Optional)
text += " (optional)";
if (!this->DefaultValue.Empty())
{
if (this->ValueType == 1)
{
if (this->DefaultValue.ToString() == "0")
text = text + ", (default: false)";
else
text = text + ", (default: true)";
}
else
text = text + ", (default: " + this->DefaultValue.ToString() + ")";
}
string value_type = "Unknown";
switch (this->ValueType)
{
case 0:
{
value_type = "String";
break;
}
case 1:
{
value_type = "Bool";
break;
}
case 2:
{
value_type = "StringList";
break;
}
case 3:
{
value_type = "Int";
break;
}
case 4:
{
value_type = "Float";
break;
}
case 5:
{
value_type = "Directory";
break;
}
case 6:
{
value_type = "File";
break;
}
case 7:
{
value_type = "Image";
}
}
text = text + ", Type: " + value_type;
if (this->Channel == mitkCommandLineParser::Input)
text = text + ", Channel: input";
else if (this->Channel == mitkCommandLineParser::Output)
text = text + ", Channel: output";
text += "\n";
return text;
}
}
// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal class
// --------------------------------------------------------------------------
class mitkCommandLineParser::ctkInternal
{
public:
ctkInternal() : Debug(false), FieldWidth(0), StrictMode(false) {}
~ctkInternal() {}
CommandLineParserArgumentDescription *argumentDescription(const string &argument);
vector<CommandLineParserArgumentDescription *> ArgumentDescriptionList;
map<string, CommandLineParserArgumentDescription *> ArgNameToArgumentDescriptionMap;
map<string, vector<CommandLineParserArgumentDescription *>> GroupToArgumentDescriptionListMap;
StringContainerType UnparsedArguments;
StringContainerType ProcessedArguments;
string ErrorString;
bool Debug;
string::size_type FieldWidth;
string LongPrefix;
string ShortPrefix;
string CurrentGroup;
string DisableQSettingsLongArg;
string DisableQSettingsShortArg;
bool StrictMode;
};
// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal methods
// --------------------------------------------------------------------------
CommandLineParserArgumentDescription *mitkCommandLineParser::ctkInternal::argumentDescription(const string &argument)
{
string unprefixedArg = argument;
if (!LongPrefix.empty() && argument.compare(0, LongPrefix.size(), LongPrefix) == 0)
{
// Case when (ShortPrefix + UnPrefixedArgument) matches LongPrefix
if (argument == LongPrefix && !ShortPrefix.empty() && argument.compare(0, ShortPrefix.size(), ShortPrefix) == 0)
{
unprefixedArg = argument.substr(ShortPrefix.size(), argument.size());
}
else
{
unprefixedArg = argument.substr(LongPrefix.size(), argument.size());
}
}
else if (!ShortPrefix.empty() && argument.compare(0, ShortPrefix.size(), ShortPrefix) == 0)
{
unprefixedArg = argument.substr(ShortPrefix.size(), argument.size());
}
else if (!LongPrefix.empty() && !ShortPrefix.empty())
{
return nullptr;
}
if (ArgNameToArgumentDescriptionMap.count(unprefixedArg))
{
return this->ArgNameToArgumentDescriptionMap[unprefixedArg];
}
return nullptr;
}
// --------------------------------------------------------------------------
// ctkCommandLineParser methods
// --------------------------------------------------------------------------
mitkCommandLineParser::mitkCommandLineParser()
{
this->Internal = new ctkInternal();
this->Category = string();
this->Title = string();
this->Contributor = string();
this->Description = string();
this->ParameterGroupName = "Parameters";
this->ParameterGroupDescription = "Parameters";
}
// --------------------------------------------------------------------------
mitkCommandLineParser::~mitkCommandLineParser()
{
delete this->Internal;
}
// --------------------------------------------------------------------------
map<string, us::Any> mitkCommandLineParser::parseArguments(const StringContainerType &arguments, bool *ok)
{
// Reset
this->Internal->UnparsedArguments.clear();
this->Internal->ProcessedArguments.clear();
this->Internal->ErrorString.clear();
// foreach (CommandLineParserArgumentDescription* desc, this->Internal->ArgumentDescriptionList)
for (unsigned int i = 0; i < Internal->ArgumentDescriptionList.size(); i++)
{
CommandLineParserArgumentDescription *desc = Internal->ArgumentDescriptionList.at(i);
desc->Value = us::Any(desc->ValueType);
if (!desc->DefaultValue.Empty())
{
desc->Value = desc->DefaultValue;
}
}
bool error = false;
bool ignoreRest = false;
CommandLineParserArgumentDescription *currentArgDesc = nullptr;
vector<CommandLineParserArgumentDescription *> parsedArgDescriptions;
for (unsigned int i = 1; i < arguments.size(); ++i)
{
string argument = arguments.at(i);
if (this->Internal->Debug)
{
std::cout << "Processing" << argument;
}
if (!argument.compare("--version"))
{
std::cout << "Git commit hash: " << MITK_REVISION << std::endl;
std::cout << "Git branch name: " << MITK_REVISION_NAME << "\n" << std::endl;
}
if (!argument.compare("--xml") || !argument.compare("-xml") || !argument.compare("--XML") ||
!argument.compare("-XML"))
{
this->generateXmlOutput();
return map<string, us::Any>();
}
// should argument be ignored ?
if (ignoreRest)
{
if (this->Internal->Debug)
{
std::cout << " Skipping: IgnoreRest flag was been set";
}
this->Internal->UnparsedArguments.push_back(argument);
continue;
}
// Skip if the argument does not start with the defined prefix
if (!(argument.compare(0, Internal->LongPrefix.size(), Internal->LongPrefix) == 0 ||
argument.compare(0, Internal->ShortPrefix.size(), Internal->ShortPrefix) == 0))
{
if (this->Internal->StrictMode)
{
this->Internal->ErrorString = "Unknown argument ";
this->Internal->ErrorString += argument;
error = true;
break;
}
if (this->Internal->Debug)
{
std::cout << " Skipping: It does not start with the defined prefix";
}
this->Internal->UnparsedArguments.push_back(argument);
continue;
}
// Skip if argument has already been parsed ...
bool alreadyProcessed = false;
for (const auto &alreadyHandledArgument : Internal->ProcessedArguments)
if (argument.compare(alreadyHandledArgument) == 0)
{
alreadyProcessed = true;
break;
}
if (alreadyProcessed)
{
if (this->Internal->StrictMode)
{
this->Internal->ErrorString = "Argument ";
this->Internal->ErrorString += argument;
this->Internal->ErrorString += " already processed !";
error = true;
break;
}
if (this->Internal->Debug)
{
std::cout << " Skipping: Already processed !";
}
continue;
}
// Retrieve corresponding argument description
currentArgDesc = this->Internal->argumentDescription(argument);
// Is there a corresponding argument description ?
if (currentArgDesc)
{
// If the argument is deprecated, print the help text but continue processing
if (currentArgDesc->Deprecated)
{
std::cout << "Deprecated argument " << argument << ": " << currentArgDesc->ArgHelp;
}
else
{
parsedArgDescriptions.push_back(currentArgDesc);
}
this->Internal->ProcessedArguments.push_back(currentArgDesc->ShortArg);
this->Internal->ProcessedArguments.push_back(currentArgDesc->LongArg);
int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
ignoreRest = currentArgDesc->IgnoreRest;
if (this->Internal->Debug && ignoreRest)
{
std::cout << " IgnoreRest flag is True";
}
// Is the number of parameters associated with the argument being processed known ?
if (numberOfParametersToProcess == 0)
{
currentArgDesc->addParameter("true");
}
else if (numberOfParametersToProcess > 0)
{
- string missingParameterError = "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
+ string missingParameterError = "Argument %1 has %2 value(s) associated whereas exactly %3 are expected.";
for (int j = 1; j <= numberOfParametersToProcess; ++j)
{
if (i + j >= arguments.size())
{
// this->Internal->ErrorString =
// missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
string parameter = arguments.at(i + j);
if (this->Internal->Debug)
{
std::cout << " Processing parameter" << j << ", value:" << parameter;
}
if (this->argumentAdded(parameter))
{
// this->Internal->ErrorString =
// missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
if (!currentArgDesc->addParameter(parameter))
{
// this->Internal->ErrorString = string(
// "Value(s) associated with argument %1 are incorrect. %2").
// arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
}
// Update main loop increment
i = i + numberOfParametersToProcess;
}
else if (numberOfParametersToProcess == -1)
{
if (this->Internal->Debug)
{
std::cout << " Proccessing StringList ...";
}
int j = 1;
while (j + i < arguments.size())
{
if (this->argumentAdded(arguments.at(j + i)))
{
if (this->Internal->Debug)
{
std::cout << " No more parameter for" << argument;
}
break;
}
string parameter = arguments.at(j + i);
if (parameter.compare(0, Internal->LongPrefix.size(), Internal->LongPrefix) == 0 ||
parameter.compare(0, Internal->ShortPrefix.size(), Internal->ShortPrefix) == 0)
{
j--;
break;
}
if (this->Internal->Debug)
{
std::cout << " Processing parameter" << j << ", value:" << parameter;
}
if (!currentArgDesc->addParameter(parameter))
{
// this->Internal->ErrorString = string(
// "Value(s) associated with argument %1 are incorrect. %2").
// arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
j++;
}
// Update main loop increment
i = i + j;
}
}
else
{
if (this->Internal->StrictMode)
{
this->Internal->ErrorString = "Unknown argument ";
this->Internal->ErrorString += argument;
error = true;
break;
}
if (this->Internal->Debug)
{
std::cout << " Skipping: Unknown argument";
}
this->Internal->UnparsedArguments.push_back(argument);
}
}
if (ok)
{
*ok = !error;
}
map<string, us::Any> parsedArguments;
int obligatoryArgs = 0;
vector<CommandLineParserArgumentDescription *>::iterator it;
for (it = Internal->ArgumentDescriptionList.begin(); it != Internal->ArgumentDescriptionList.end(); ++it)
{
CommandLineParserArgumentDescription *desc = *it;
if (!desc->Optional)
obligatoryArgs++;
}
int parsedObligatoryArgs = 0;
for (it = parsedArgDescriptions.begin(); it != parsedArgDescriptions.end(); ++it)
{
CommandLineParserArgumentDescription *desc = *it;
string key;
if (!desc->LongArg.empty())
{
key = desc->LongArg;
}
else
{
key = desc->ShortArg;
}
if (!desc->Optional)
parsedObligatoryArgs++;
std::pair<string, us::Any> elem;
elem.first = key;
elem.second = desc->Value;
parsedArguments.insert(elem);
}
if (obligatoryArgs > parsedObligatoryArgs)
{
parsedArguments.clear();
cout << helpText();
}
return parsedArguments;
}
// -------------------------------------------------------------------------
map<string, us::Any> mitkCommandLineParser::parseArguments(int argc, char **argv, bool *ok)
{
std::cout << "Running Command Line Utility *" << Title << "*" << std::endl;
StringContainerType arguments;
// Create a StringContainerType of arguments
for (int i = 0; i < argc; ++i)
arguments.push_back(argv[i]);
return this->parseArguments(arguments, ok);
}
// -------------------------------------------------------------------------
string mitkCommandLineParser::errorString() const
{
return this->Internal->ErrorString;
}
// -------------------------------------------------------------------------
const mitkCommandLineParser::StringContainerType &mitkCommandLineParser::unparsedArguments() const
{
return this->Internal->UnparsedArguments;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::addArgument(const string &longarg,
const string &shortarg,
Type type,
const string &argLabel,
const string &argHelp,
const us::Any &defaultValue,
bool optional,
bool ignoreRest,
bool deprecated,
mitkCommandLineParser::Channel channel)
{
if (longarg.empty() && shortarg.empty())
{
return;
}
/* Make sure it's not already added */
bool added = (this->Internal->ArgNameToArgumentDescriptionMap.count(longarg) != 0);
if (added)
{
return;
}
added = (this->Internal->ArgNameToArgumentDescriptionMap.count(shortarg) != 0);
if (added)
{
return;
}
auto argDesc = new CommandLineParserArgumentDescription(longarg,
this->Internal->LongPrefix,
shortarg,
this->Internal->ShortPrefix,
type,
argHelp,
argLabel,
defaultValue,
ignoreRest,
deprecated,
optional,
channel,
ParameterGroupName,
ParameterGroupDescription);
std::string::size_type argWidth = 0;
if (!longarg.empty())
{
this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;
argWidth += longarg.size() + this->Internal->LongPrefix.size();
}
if (!shortarg.empty())
{
this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;
argWidth += shortarg.size() + this->Internal->ShortPrefix.size() + 2;
}
argWidth += 5;
// Set the field width for the arguments
if (argWidth > this->Internal->FieldWidth)
{
this->Internal->FieldWidth = argWidth;
}
this->Internal->ArgumentDescriptionList.push_back(argDesc);
this->Internal->GroupToArgumentDescriptionListMap[this->Internal->CurrentGroup].push_back(argDesc);
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::addDeprecatedArgument(const string &longarg,
const string &shortarg,
const string &argLabel,
const string &argHelp)
{
addArgument(longarg, shortarg, StringList, argLabel, argHelp, us::Any(), false, true, false);
}
// --------------------------------------------------------------------------
std::vector < std::map<std::string, us::Any> > mitkCommandLineParser::getArgumentList()
{
std::vector < std::map<std::string, us::Any> > parameterList;
//for (CommandLineParserArgumentDescription* argument : this->Internal->ArgumentDescriptionList)
for (std::size_t i = 0; i< this->Internal->ArgumentDescriptionList.size(); ++i)
{
CommandLineParserArgumentDescription* argument = this->Internal->ArgumentDescriptionList[i];
std::map<std::string, us::Any> tmpMap;
//tmpMap["helptext"] = us::Any(argument->helpText);
tmpMap["longarg"] = us::Any(argument->LongArg);
tmpMap["longargprefix"] = us::Any(argument->LongArgPrefix);
tmpMap["shortarg"] = us::Any(argument->ShortArg);
tmpMap["shortargprefix"] = us::Any(argument->ShortArgPrefix);
tmpMap["arghelp"] = us::Any(argument->ArgHelp);
tmpMap["arglabel"] = us::Any(argument->ArgLabel);
tmpMap["arggroup"] = us::Any(argument->ArgGroup);
tmpMap["arggroupdescription"] = us::Any(argument->ArgGroupDescription);
tmpMap["ignorerest"] = us::Any(argument->IgnoreRest);
tmpMap["numberofparameterstoprocess"] = us::Any(argument->NumberOfParametersToProcess);
tmpMap["deprecated"] = us::Any(argument->Deprecated);
tmpMap["optional"] = us::Any(argument->Optional);
tmpMap["defaultvalue"] = argument->DefaultValue;
tmpMap["value"] = argument->Value;
tmpMap["valuetype"] = us::Any(argument->ValueType);
tmpMap["channel"] = us::Any(argument->Channel);
parameterList.push_back(tmpMap);
}
return parameterList;
}
// --------------------------------------------------------------------------
std::string::size_type mitkCommandLineParser::fieldWidth() const
{
return this->Internal->FieldWidth;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::beginGroup(const string &description)
{
this->Internal->CurrentGroup = description;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::endGroup()
{
this->Internal->CurrentGroup.clear();
}
// --------------------------------------------------------------------------
string mitkCommandLineParser::helpText() const
{
string text;
vector<CommandLineParserArgumentDescription *> deprecatedArgs;
text = "Command Line Utility *" + Title + "* in Category *" + Category + "*\n";
text += Description + "\n";
text += Contributor + "\n\n";
text += "Use --xml to generate an XML description parsable as a CTK Command Line Module Plugin.\n";
text += "Use --version to print MITK revision information.\n";
// Loop over grouped argument descriptions
map<string, vector<CommandLineParserArgumentDescription *>>::iterator it;
for (it = Internal->GroupToArgumentDescriptionListMap.begin();
it != Internal->GroupToArgumentDescriptionListMap.end();
++it)
{
if (!(*it).first.empty())
{
text = text + "\n" + (*it).first + "\n";
}
vector<CommandLineParserArgumentDescription *>::iterator it2;
for (it2 = (*it).second.begin(); it2 != (*it).second.end(); ++it2)
{
CommandLineParserArgumentDescription *argDesc = *it2;
if (argDesc->Deprecated)
{
deprecatedArgs.push_back(argDesc);
}
else
{
text += argDesc->helpText();
}
}
}
if (!deprecatedArgs.empty())
{
text += "\nDeprecated arguments:\n";
vector<CommandLineParserArgumentDescription *>::iterator it2;
for (it2 = deprecatedArgs.begin(); it2 != deprecatedArgs.end(); ++it2)
{
CommandLineParserArgumentDescription *argDesc = *it2;
text += argDesc->helpText();
}
}
return text;
}
// --------------------------------------------------------------------------
bool mitkCommandLineParser::argumentAdded(const string &argument) const
{
return (this->Internal->ArgNameToArgumentDescriptionMap.count(argument) != 0);
}
// --------------------------------------------------------------------------
bool mitkCommandLineParser::argumentParsed(const string &argument) const
{
for (unsigned int i = 0; i < Internal->ProcessedArguments.size(); i++)
if (argument.compare(Internal->ProcessedArguments.at(i)) == 0)
return true;
return false;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::setArgumentPrefix(const string &longPrefix, const string &shortPrefix)
{
this->Internal->LongPrefix = longPrefix;
this->Internal->ShortPrefix = shortPrefix;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::setStrictModeEnabled(bool strictMode)
{
this->Internal->StrictMode = strictMode;
}
void mitkCommandLineParser::generateXmlOutput()
{
std::stringstream xml;
xml << "<executable>" << endl;
xml << "<category>" << Category << "</category>" << endl;
xml << "<title>" << Title << "</title>" << endl;
xml << "<description>" << Description << "</description>" << endl;
xml << "<contributor>" << Contributor << "</contributor>" << endl;
xml << "<parameters>" << endl;
std::vector<CommandLineParserArgumentDescription *>::iterator it;
std::string lastParameterGroup = "";
for (it = this->Internal->ArgumentDescriptionList.begin(); it != this->Internal->ArgumentDescriptionList.end(); it++)
{
std::string type;
switch ((*it)->ValueType)
{
case mitkCommandLineParser::String:
type = "string";
break;
case mitkCommandLineParser::Bool:
type = "boolean";
break;
case mitkCommandLineParser::StringList:
type = "string-vector";
break;
case mitkCommandLineParser::Int:
type = "integer";
break;
case mitkCommandLineParser::Float:
type = "float";
break;
case mitkCommandLineParser::Directory:
type = "directory";
break;
case mitkCommandLineParser::Image:
type = "image";
break;
case mitkCommandLineParser::File:
type = "file";
break;
}
if (lastParameterGroup.compare((*it)->ArgGroup))
{
if (it != this->Internal->ArgumentDescriptionList.begin())
{
xml << "</parameters>" << endl;
xml << "<parameters>" << endl;
}
xml << "<label>" << (*it)->ArgGroup << "</label>" << endl;
xml << "<description>" << (*it)->ArgGroupDescription << "</description>" << endl;
lastParameterGroup = (*it)->ArgGroup;
}
// Skip help item, as it's no use in GUI
if ((*it)->ShortArg == "h")
continue;
auto name = (*it)->LongArg;
if (name.empty())
name = (*it)->ShortArg;
xml << "<" << type << ">" << endl;
xml << "<name>" << name << "</name>" << endl;
xml << "<description>" << (*it)->ArgHelp << "</description>" << endl;
xml << "<label>" << (*it)->ArgLabel << "</label>" << endl;
if (!(*it)->DefaultValue.Empty())
xml << "<default>" << (*it)->DefaultValue.ToString() << "</default>" << endl;
xml << "<longflag>" << (*it)->LongArg << "</longflag>" << endl;
xml << "<flag>" << (*it)->ShortArg << "</flag>" << endl;
if (((*it)->ValueType == mitkCommandLineParser::File ||
(*it)->ValueType == mitkCommandLineParser::Directory ||
(*it)->ValueType == mitkCommandLineParser::Image) &&
(*it)->Channel == mitkCommandLineParser::Channel::Input)
{
xml << "<channel>input</channel>" << endl;
}
else if (((*it)->ValueType == mitkCommandLineParser::File ||
(*it)->ValueType == mitkCommandLineParser::Directory ||
(*it)->ValueType == mitkCommandLineParser::Image) &&
(*it)->Channel == mitkCommandLineParser::Channel::Output)
{
xml << "<channel>output</channel>" << endl;
}
else if ((*it)->Channel == mitkCommandLineParser::Channel::Output ||
(*it)->Channel == mitkCommandLineParser::Channel::Input)
{
std::cout << "Only the types Directory, File or Image may be flagged as Input or Output! Ignoring flag for parameter " + name << std::endl;
}
xml << "</" << type << ">" << endl;
}
xml << "</parameters>" << endl;
xml << "</executable>" << endl;
cout << xml.str();
}
void mitkCommandLineParser::setTitle(string title)
{
Title = title;
}
void mitkCommandLineParser::setContributor(string contributor)
{
Contributor = contributor;
}
void mitkCommandLineParser::setCategory(string category)
{
Category = category;
}
void mitkCommandLineParser::setDescription(string description)
{
Description = description;
}
void mitkCommandLineParser::changeParameterGroup(string name, string tooltip)
{
ParameterGroupName = name;
ParameterGroupDescription = tooltip;
}
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h b/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h
index c76138b687..2ed42a9bf2 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h
@@ -1,62 +1,62 @@
/*============================================================================
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 mitkContourModelSetSource_h
#define mitkContourModelSetSource_h
#include "mitkBaseDataSource.h"
#include "mitkContourModelSet.h"
#include <MitkContourModelExports.h>
namespace mitk
{
/**
* @brief Superclass of all classes generating ContourModels.
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelSetSource : public BaseDataSource
{
public:
mitkClassMacro(ContourModelSetSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef ContourModelSet OutputType;
typedef OutputType::Pointer OutputTypePointer;
mitkBaseDataSourceGetOutputDeclarations
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
ContourModelSetSource();
~ContourModelSetSource() override;
};
}
#endif
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSource.h b/Modules/ContourModel/Algorithms/mitkContourModelSource.h
index 89d1925adc..393b729e6d 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSource.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSource.h
@@ -1,62 +1,62 @@
/*============================================================================
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 mitkContourModelSource_h
#define mitkContourModelSource_h
#include "mitkBaseDataSource.h"
#include "mitkContourModel.h"
#include <MitkContourModelExports.h>
namespace mitk
{
/**
* @brief Superclass of all classes generating ContourModels.
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelSource : public BaseDataSource
{
public:
mitkClassMacro(ContourModelSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self)
typedef ContourModel OutputType;
typedef OutputType::Pointer OutputTypePointer;
mitkBaseDataSourceGetOutputDeclarations
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
ContourModelSource();
~ContourModelSource() override;
};
}
#endif
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp
index a7840e854f..89693bebc5 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp
@@ -1,202 +1,202 @@
/*============================================================================
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 "mitkContourModelSubDivisionFilter.h"
#include <mitkInteractionConst.h>
#include <mitkPointOperation.h>
mitk::ContourModelSubDivisionFilter::ContourModelSubDivisionFilter()
{
OutputType::Pointer output = dynamic_cast<OutputType *>(this->MakeOutput(0).GetPointer());
this->SetNumberOfRequiredInputs(1);
this->SetNumberOfIndexedOutputs(1);
this->SetNthOutput(0, output.GetPointer());
this->m_InterpolationIterations = 4;
}
mitk::ContourModelSubDivisionFilter::~ContourModelSubDivisionFilter()
{
}
void mitk::ContourModelSubDivisionFilter::SetInput(const mitk::ContourModelSubDivisionFilter::InputType *input)
{
this->SetInput(0, input);
}
void mitk::ContourModelSubDivisionFilter::SetInput(unsigned int idx,
const mitk::ContourModelSubDivisionFilter::InputType *input)
{
if (idx + 1 > this->GetNumberOfInputs())
{
this->SetNumberOfRequiredInputs(idx + 1);
}
if (input != static_cast<InputType *>(this->ProcessObject::GetInput(idx)))
{
this->ProcessObject::SetNthInput(idx, const_cast<InputType *>(input));
this->Modified();
}
}
const mitk::ContourModelSubDivisionFilter::InputType *mitk::ContourModelSubDivisionFilter::GetInput(void)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelSubDivisionFilter::InputType *>(this->ProcessObject::GetInput(0));
}
const mitk::ContourModelSubDivisionFilter::InputType *mitk::ContourModelSubDivisionFilter::GetInput(unsigned int idx)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelSubDivisionFilter::InputType *>(this->ProcessObject::GetInput(idx));
}
void mitk::ContourModelSubDivisionFilter::GenerateData()
{
mitk::ContourModel::Pointer input = const_cast<mitk::ContourModel *>(this->GetInput(0));
// mitk::ContourModelSubDivisionFilter::OutputType::Pointer outputContour = this->GetOutput();
mitk::ContourModel::Pointer contour(input);
auto timestep = static_cast<TimeStepType>(input->GetTimeSteps());
for (decltype(timestep) currentTimestep = 0; currentTimestep < timestep; currentTimestep++)
{
if (input->GetNumberOfVertices(currentTimestep) >= 4)
{
for (int iterations = 0; iterations < this->m_InterpolationIterations; iterations++)
{
auto it = contour->IteratorBegin();
auto end = contour->IteratorEnd();
auto first = contour->IteratorBegin();
auto last = contour->IteratorEnd() - 1;
// tempory contour to store result of a subdivision iteration
mitk::ContourModel::Pointer tempContour = mitk::ContourModel::New();
// insert subpoints
while (it != end)
{
// add the current point to the temp contour
tempContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, currentTimestep);
// control points for interpolation
auto Ci = it;
InputType::VertexIterator CiPlus1;
InputType::VertexIterator CiPlus2;
InputType::VertexIterator CiMinus1;
// consider all possible cases
if (it == first)
{
if (input->IsClosed(currentTimestep))
{
CiPlus1 = it + 1;
CiPlus2 = it + 2;
CiMinus1 = last;
}
else
{
CiPlus1 = it + 1;
CiPlus2 = it + 2;
CiMinus1 = it;
}
}
else if (it == last)
{
if (input->IsClosed(currentTimestep))
{
CiPlus1 = first;
CiPlus2 = first + 1;
CiMinus1 = it - 1;
}
else
{
// don't add point after last
break;
}
}
else if (it == (last - 1))
{
if (input->IsClosed(currentTimestep))
{
CiPlus1 = it + 1;
CiPlus2 = first;
CiMinus1 = it - 1;
}
else
{
CiPlus1 = it + 1;
CiPlus2 = it + 1;
CiMinus1 = it - 1;
}
}
else
{
CiPlus1 = it + 1;
CiPlus2 = it + 2;
CiMinus1 = it - 1;
}
/* F2i = Ci
* F2i+1 = -1/16Ci-1 + 9/16Ci + 9/16Ci+1 - 1/16Ci+2
*/
mitk::Point3D subpoint;
mitk::Point3D a;
a[0] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[0];
a[1] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[1];
a[2] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[2];
mitk::Point3D b;
b[0] = (9.0 / 16.0) * (*Ci)->Coordinates[0];
b[1] = (9.0 / 16.0) * (*Ci)->Coordinates[1];
b[2] = (9.0 / 16.0) * (*Ci)->Coordinates[2];
mitk::Point3D c;
c[0] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[0];
c[1] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[1];
c[2] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[2];
mitk::Point3D d;
d[0] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[0];
d[1] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[1];
d[2] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[2];
subpoint[0] = a[0] + b[0] + c[0] + d[0];
subpoint[1] = a[1] + b[1] + c[1] + d[1];
subpoint[2] = a[2] + b[2] + c[2] + d[2];
InputType::VertexType subdivisionPoint(subpoint, false);
// add the new subdivision point to our tempContour
tempContour->AddVertex(subdivisionPoint.Coordinates, currentTimestep);
it++;
}
// set the interpolated contour as the contour for the next iteration
contour = tempContour;
}
}
else
{
- // filter not executeable - set input to output
+ // filter not executable - set input to output
contour = input;
}
}
// somehow the isClosed property is not set via copy constructor
contour->SetClosed(input->IsClosed());
this->SetNthOutput(0, contour);
}
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h
index f52edbc290..0fd4f1a269 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkContourModelSubDivisionFilter_h
#define mitkContourModelSubDivisionFilter_h
#include "mitkCommon.h"
#include "mitkContourModel.h"
#include "mitkContourModelSource.h"
#include <MitkContourModelExports.h>
namespace mitk
{
/**
*
* \brief This filter interpolates a subdivision curve between control points of the contour.
* For inserting subpoints Dyn-Levin-Gregory (DLG) interpolation scheme is used.
* Interpolating a cruve subdivision is done by:
* F2i = Ci
* F2i+1 = -1/16Ci-1 + 9/16Ci + 9/16Ci+1 - 1/16Ci+2
*
- * The number of interpolation iterations can be set via SetNumberOfIterations(int) which are 4 by dafault.
+ * The number of interpolation iterations can be set via SetNumberOfIterations(int) which are 4 by default.
*
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelSubDivisionFilter : public ContourModelSource
{
public:
mitkClassMacro(ContourModelSubDivisionFilter, ContourModelSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef ContourModel OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef mitk::ContourModel InputType;
/**
* \brief Set the number of iterations for inserting new interpolated control points.
*
*/
void SetNumberOfIterations(int iterations) { this->m_InterpolationIterations = iterations; }
using Superclass::SetInput;
virtual void SetInput(const InputType *input);
virtual void SetInput(unsigned int idx, const InputType *input);
const InputType *GetInput(void);
const InputType *GetInput(unsigned int idx);
protected:
ContourModelSubDivisionFilter();
~ContourModelSubDivisionFilter() override;
void GenerateOutputInformation() override{};
void GenerateData() override;
int m_InterpolationIterations;
};
}
#endif
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp b/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp
index 93dde7008c..31897208cc 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp
+++ b/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp
@@ -1,145 +1,145 @@
/*============================================================================
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 <mitkContourModelToSurfaceFilter.h>
#include <mitkSurface.h>
#include <vtkCellArray.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolygon.h>
#include <vtkSmartPointer.h>
mitk::ContourModelToSurfaceFilter::ContourModelToSurfaceFilter()
{
this->SetNthOutput(0, mitk::Surface::New().GetPointer());
}
mitk::ContourModelToSurfaceFilter::~ContourModelToSurfaceFilter()
{
}
void mitk::ContourModelToSurfaceFilter::GenerateOutputInformation()
{
}
void mitk::ContourModelToSurfaceFilter::SetInput(const mitk::ContourModelToSurfaceFilter::InputType *input)
{
this->SetInput(0, input);
}
void mitk::ContourModelToSurfaceFilter::SetInput(unsigned int idx,
const mitk::ContourModelToSurfaceFilter::InputType *input)
{
if (idx + 1 > this->GetNumberOfInputs())
{
this->SetNumberOfRequiredInputs(idx + 1);
}
if (input != static_cast<InputType *>(this->ProcessObject::GetInput(idx)))
{
this->ProcessObject::SetNthInput(idx, const_cast<InputType *>(input));
this->Modified();
}
}
const mitk::ContourModelToSurfaceFilter::InputType *mitk::ContourModelToSurfaceFilter::GetInput(void)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelToSurfaceFilter::InputType *>(this->ProcessObject::GetInput(0));
}
const mitk::ContourModelToSurfaceFilter::InputType *mitk::ContourModelToSurfaceFilter::GetInput(unsigned int idx)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelToSurfaceFilter::InputType *>(this->ProcessObject::GetInput(idx));
}
void mitk::ContourModelToSurfaceFilter::GenerateData()
{
mitk::Surface *surface = this->GetOutput();
auto *inputContour = (mitk::ContourModel *)GetInput();
unsigned int numberOfTimeSteps = inputContour->GetTimeSteps();
surface->Expand(numberOfTimeSteps);
for (unsigned int currentTimeStep = 0; currentTimeStep < numberOfTimeSteps; currentTimeStep++)
{
/* First of all convert the control points of the contourModel to vtk points
* and add lines in between them
*/
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw
vtkSmartPointer<vtkCellArray> polygons = vtkSmartPointer<vtkCellArray>::New();
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points
// if the contour has less than 3 points, set empty PolyData for current timestep
// polygon needs at least 3 points
if (inputContour->GetNumberOfVertices(currentTimeStep) <= 2)
{
vtkSmartPointer<vtkPolyData> emptyPolyData = vtkSmartPointer<vtkPolyData>::New();
surface->SetVtkPolyData(emptyPolyData, currentTimeStep);
continue;
}
// iterate over all control points
auto current = inputContour->IteratorBegin(currentTimeStep);
auto end = inputContour->IteratorEnd(currentTimeStep);
vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();
polygon->GetPointIds()->SetNumberOfIds(inputContour->GetNumberOfVertices(currentTimeStep));
int j(0);
while (current != end)
{
mitk::ContourModel::VertexType *currentPoint = *current;
vtkIdType id = points->InsertNextPoint(
currentPoint->Coordinates[0], currentPoint->Coordinates[1], currentPoint->Coordinates[2]);
polygon->GetPointIds()->SetId(j, id);
// create connections between the points
- // no previous point for first point available (ingnore id=0)
+ // no previous point for first point available (ignore id=0)
if (id > 0)
{
lines->InsertNextCell(2);
lines->InsertCellPoint(id - 1);
lines->InsertCellPoint(id);
}
current++;
j++;
}
/*
* If the contour is closed an additional line has to be created between the first point
* and the last point
*/
if (inputContour->IsClosed(currentTimeStep))
{
lines->InsertNextCell(2);
lines->InsertCellPoint(0);
lines->InsertCellPoint((inputContour->GetNumberOfVertices(currentTimeStep) - 1));
}
polygons->InsertNextCell(polygon);
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// Add the points to the dataset
polyData->SetPoints(points);
polyData->SetPolys(polygons);
polyData->SetLines(lines);
polyData->BuildLinks();
surface->SetVtkPolyData(polyData, currentTimeStep);
}
}
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
index 769634ae1d..29ccd2919f 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
@@ -1,155 +1,155 @@
/*============================================================================
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 mitkContourModelUtils_h
#define mitkContourModelUtils_h
#include <mitkContourModel.h>
#include <mitkImage.h>
#include <vtkSmartPointer.h>
#include <MitkContourModelExports.h>
namespace mitk
{
/**
* \brief Helpful methods for working with contours and images
*
*
*/
class MITKCONTOURMODEL_EXPORT ContourModelUtils : public itk::Object
{
public:
mitkClassMacroItkParent(ContourModelUtils, itk::Object);
/**
\brief Projects a contour onto an image point by point. Converts from world to index coordinates.
\param slice
\param contourIn3D
*/
static ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice,
const ContourModel *contourIn3D);
/**
\brief Projects a slice index coordinates of a contour back into world coordinates.
\param sliceGeometry
\param contourIn2D
*/
static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry,
const ContourModel *contourIn2D);
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This version always uses the contour of time step 0 and fills the image.
- \deprecated Ths function is deprecated. Use FillContourInSlice2() (in
+ \deprecated This function is deprecated. Use FillContourInSlice2() (in
conjunction e.g. with TransferLabelContent()) instead.
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
//[[deprecated]]
DEPRECATED(static void FillContourInSlice(const ContourModel *projectedContour,
Image *sliceImage,
const Image* workingImage,
int paintingPixelValue = 1));
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This overloaded version uses the contour at the passed contourTimeStep
to fill the passed image slice.
- \deprecated Ths function is deprecated. Use FillContourInSlice2() (in
+ \deprecated This function is deprecated. Use FillContourInSlice2() (in
conjunction e.g. with TransferLabelContentAtTimeStep()) instead.
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
//[[deprecated]]
DEPRECATED(static void FillContourInSlice(const ContourModel *projectedContour,
TimeStepType contourTimeStep,
Image *sliceImage,
const Image* workingImage,
int paintingPixelValue = 1));
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This version always uses the contour of time step 0 and fills the image.
\param projectedContour Pointer to the contour that should be projected.
\param sliceImage Pointer to the image which content should be altered by
adding the contour with the specified paintingPixelValue.
\param paintingPixelValue
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
static void FillContourInSlice2(const ContourModel* projectedContour,
Image* sliceImage,
int paintingPixelValue = 1);
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This overloaded version uses the contour at the passed contourTimeStep
to fill the passed image slice.
\param projectedContour Pointer to the contour that should be projected.
\param contourTimeStep
\param sliceImage Pointer to the image which content should be altered by
\param paintingPixelValue
adding the contour with the specified paintingPixelValue.
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
static void FillContourInSlice2(const ContourModel* projectedContour,
TimeStepType contourTimeStep,
Image* sliceImage,
int paintingPixelValue = 1);
/**
\brief Fills the paintingPixelValue into every pixel of resultImage as indicated by filledImage.
If a LableSet image is specified it also by incorporating the rules of LabelSet images when filling the content.
- \param filledImage Pointer to the image content that should be checked to decied of a pixel in resultImage should
+ \param filledImage Pointer to the image content that should be checked to decide if a pixel in resultImage should
be filled with paintingPixelValue or not.
\param resultImage Pointer to the image content that should be overwritten guided by the content of filledImage.
\param image Pointer to an mitk image that allows to define the LabelSet image which states steer the filling process.
If an LabelSet instance is passed its states (e.g. locked labels etc...) will be used. If nullptr or an normal image
is passed, then simply any pixel position indicated by filledImage will be overwritten.
\param paintingPixelValue the pixelvalue/label that should be used in the result image when filling.
\param fillForegroundThreshold The threshold value that decides if a pixel in the filled image counts
as foreground (>=fillForegroundThreshold) or not.
- \deprecated Ths function is deprecated. Use TransferLabelContent() instead.
+ \deprecated This function is deprecated. Use TransferLabelContent() instead.
*/
[[deprecated]]
static void FillSliceInSlice(vtkSmartPointer<vtkImageData> filledImage,
vtkSmartPointer<vtkImageData> resultImage,
const Image* image,
int paintingPixelValue,
double fillForegroundThreshold = 1.0);
/**
\brief Move the contour in time step 0 to to a new contour model at the given time step.
*/
static ContourModel::Pointer MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType timeStep);
/**
\brief Retrieves the active pixel value of a (labelset) image.
If the image is basic image, the pixel value 1 (one) will be returned.
If the image is actually a labelset image, the pixel value of the active label of the active layer will be
returned.
\param workingImage The (labelset) image to retrieve the active pixel value of.
*/
static int GetActivePixelValue(const Image* workingImage);
protected:
ContourModelUtils();
~ContourModelUtils() override;
};
}
#endif
diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h
index f0029e9f0a..5aab226c8c 100644
--- a/Modules/ContourModel/DataManagement/mitkContourElement.h
+++ b/Modules/ContourModel/DataManagement/mitkContourElement.h
@@ -1,308 +1,308 @@
/*============================================================================
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 mitkContourElement_h
#define mitkContourElement_h
#include "mitkCommon.h"
#include <MitkContourModelExports.h>
#include <mitkNumericTypes.h>
#include <deque>
namespace mitk
{
/** \brief Represents a contour in 3D space.
A ContourElement is consisting of linked vertices implicitely defining the contour.
They are stored in a double ended queue making it possible to add vertices at front and
end of the contour and to iterate in both directions.
To mark a vertex as a special one it can be set as a control point.
\note This class assumes that it manages its vertices. So if a vertex instance is added to this
- class the ownership of the vertex is transfered to the ContourElement instance.
+ class the ownership of the vertex is transferred to the ContourElement instance.
The ContourElement instance takes care of deleting vertex instances if needed.
It is highly not recommend to use this class directly as it is designed as a internal class of
- ContourModel. Therefore it is adviced to use ContourModel if contour representations are needed in
+ ContourModel. Therefore it is advised to use ContourModel if contour representations are needed in
MITK.
*/
class MITKCONTOURMODEL_EXPORT ContourElement : public itk::LightObject
{
public:
mitkClassMacroItkParent(ContourElement, itk::LightObject);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Represents a single vertex of a contour.
*/
struct MITKCONTOURMODEL_EXPORT ContourModelVertex
{
ContourModelVertex(const mitk::Point3D& point, bool active = false) : IsControlPoint(active), Coordinates(point) {};
ContourModelVertex(const ContourModelVertex& other)
: IsControlPoint(other.IsControlPoint), Coordinates(other.Coordinates)
{
};
/** \brief Treat point special. */
bool IsControlPoint;
/** \brief Coordinates in 3D space. */
mitk::Point3D Coordinates;
bool operator ==(const ContourModelVertex& other) const;
};
using VertexType = ContourModelVertex;
using VertexListType = std::deque<VertexType*>;
using VertexIterator = VertexListType::iterator;
using ConstVertexIterator = VertexListType::const_iterator;
using VertexSizeType = VertexListType::size_type;
/**Indicates an invalid index.
* It is always the maximum of the unsigned int type.*/
static const VertexSizeType NPOS = -1;
/** \brief Return a const iterator a the front.
*/
ConstVertexIterator ConstIteratorBegin() const;
/** \brief Return a const iterator a the end.
*/
ConstVertexIterator ConstIteratorEnd() const;
/** \brief Return an iterator a the front.
*/
VertexIterator IteratorBegin();
/** \brief Return an iterator a the end.
*/
VertexIterator IteratorEnd();
/** \brief Return a const iterator a the front.
* For easier support of stl functionality.
*/
ConstVertexIterator begin() const;
/** \brief Return a const iterator a the end.
* For easier support of stl functionality.
*/
ConstVertexIterator end() const;
/** \brief Return an iterator a the front.
* For easier support of stl functionality.
*/
VertexIterator begin();
/** \brief Return an iterator a the end.
* For easier support of stl functionality.
*/
VertexIterator end();
/** \brief Returns the number of contained vertices.
*/
VertexSizeType GetSize() const;
/** \brief Add a vertex at the end of the contour
\param point - coordinates in 3D space.
\param isControlPoint - is the vertex a special control point.
*/
void AddVertex(const mitk::Point3D &point, bool isControlPoint);
/** \brief Add a vertex at the front of the contour
\param point - coordinates in 3D space.
\param isControlPoint - is the vertex a control point.
*/
void AddVertexAtFront(const mitk::Point3D &point, bool isControlPoint);
/** \brief Add a vertex at a given index of the contour
\param point - coordinates in 3D space.
\param isControlPoint - is the vertex a special control point.
\param index - the index to be inserted at.
*/
void InsertVertexAtIndex(const mitk::Point3D &point, bool isControlPoint, VertexSizeType index);
/** \brief Set coordinates a given index.
\param pointId Index of vertex.
\param point Coordinates.
*/
void SetVertexAt(VertexSizeType pointId, const mitk::Point3D &point);
/** \brief Set vertex a given index (by copying the values).
\param pointId Index of vertex.
\param vertex Vertex.
\pre Passed vertex is a valid instance
*/
void SetVertexAt(VertexSizeType pointId, const VertexType* vertex);
/** \brief Returns the vertex a given index
\param index
\pre index must be valid.
*/
VertexType* GetVertexAt(VertexSizeType index);
const VertexType* GetVertexAt(VertexSizeType index) const;
/** \brief Returns the approximate nearest vertex a given position in 3D space
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the next vertex to the approximate nearest vertex of a given position in 3D space
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetNextControlVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the previous vertex to the approximate nearest vertex of a given position in 3D space
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetPreviousControlVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the approximate nearest control vertex a given posoition in 3D space, if the clicked position is within a specific range.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
VertexType *GetControlVertexAt(const mitk::Point3D &point, float eps);
/** \brief Returns the index of the given vertex within the contour.
\param vertex - the vertex to be searched.
\return index of vertex. Returns ContourElement::NPOS if not found.
*/
VertexSizeType GetIndex(const VertexType *vertex) const;
/** \brief Returns the container of the vertices.
*/
const VertexListType *GetVertexList() const;
/** \brief Returns whether the contour element is empty.
*/
bool IsEmpty() const;
/** \brief Returns if the conour is closed or not.
*/
bool IsClosed() const;
/** \brief Returns whether a given point is near a contour, according to eps.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm.
*/
bool IsNearContour(const mitk::Point3D &point, float eps) const;
/** Function that searches for the line segment of the contour that is closest to the passed point
and close enough (distance between point and line segment <= eps). If such an line segment exist,
the starting vertex and closing vertex of the found segment are passed back.
@return True indicates that a line segment was found. False indicates that no segment of the contour
is close enough to the passed point.
@remark previousVertex and nextVertex are only valid if return is true.*/
bool GetLineSegmentForPoint(const mitk::Point3D &point,
float eps,
mitk::ContourElement::VertexType *previousVertex,
mitk::ContourElement::VertexType *nextVertex) const;
/** Overloaded version that offers additional options when searching for the line segment.
In contrast to the other version it returns the index of the segment start and end as well as the point
on the line segment closest to the passed point. Further one can decide if the function should search for
the first segment that is close enough (see eps) or for the segment that is really the closest (findClosest==true).
@remark segmentStartIndex, segmentEndIndex and closestContourPoint are only valid if return is true.*/
bool GetLineSegmentForPoint(const mitk::Point3D& point,
float eps, VertexSizeType& segmentStartIndex, VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint, bool findClosest = true) const;
/** \brief Close the contour.
Connect first with last element.
*/
void Close();
/** \brief Open the contour.
Disconnect first and last element.
*/
void Open();
/** \brief Set the contours IsClosed property.
\param isClosed - true = closed; false = open;
*/
void SetClosed(bool isClosed);
/** \brief Concatenate the contuor with a another contour.
All vertices of the other contour will be cloned and added after last vertex.
\param other - the other contour
\param check - set it true to avoid adding of vertices that are already in the source contour
*/
void Concatenate(const mitk::ContourElement *other, bool check);
/** \brief Remove the given vertex from the container if exists.
\param vertex - the vertex to be removed.
*/
bool RemoveVertex(const VertexType *vertex);
/** \brief Remove a vertex at given index within the container if exists.
\param index - the index where the vertex should be removed.
*/
bool RemoveVertexAt(VertexSizeType index);
/** \brief Remove the approximate nearest vertex at given position in 3D space if one exists.
\param point - query point in 3D space.
\param eps - error bound for search algorithm.
*/
bool RemoveVertexAt(const mitk::Point3D &point, double eps);
/** \brief Clear the storage container.
*/
void Clear();
/** \brief Returns the approximate nearest vertex a given position in 3D space. With the parameter 'isControlPoint',
one can decide if any vertex should be returned, or just control vertices.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm. It is an open boundary.
\param isControlPoint
\param offset - a offset to the vertex, e.g. 1 if the next vertex should be returned or -1 for the previous vertex
*/
VertexType *BruteForceGetVertexAt(const mitk::Point3D &point, double eps, bool isControlPoint = false, int offset = 0);
/** \brief Returns the index of the approximate nearest vertex of a given position in 3D space.
\param point - query position in 3D space.
\param eps - the error bound for search algorithm. It is an open boundary.
\param verticesList - the vertex list to search the index in, either only control vertices or all vertices
*/
int BruteForceGetVertexIndexAt(const mitk::Point3D &point,
double eps,
VertexListType verticesList);
/** Returns a list pointing to all vertices that are indicated to be control
points.
\remark It is important to note, that the vertex pointers in the returned
- list directly point to the vertices stored interanlly. So they are still
+ list directly point to the vertices stored internally. So they are still
owned by the ContourElement instance that returns the list. If one wants
to take over ownership, one has to clone the vertex instances.
*/
VertexListType GetControlVertices() const;
/** \brief Uniformly redistribute control points with a given period (in number of vertices)
\param vertex - the vertex around which the redistribution is done.
\param period - number of vertices between control points.
*/
void RedistributeControlVertices(const VertexType *vertex, int period);
protected:
mitkCloneMacro(Self);
ContourElement() = default;
ContourElement(const mitk::ContourElement &other);
~ContourElement();
ContourElement& operator = (const ContourElement & other);
/** Internal helper function to correctly remove the element indicated by the iterator
from the list. After the call the iterator is invalid.
Caller of the function must ensure that the iterator is valid!.
\result Indicates if the element indicated by the iterator was removed. If iterator points to end it returns false.*/
bool RemoveVertexByIterator(VertexListType::iterator& iter);
VertexListType m_Vertices; // double ended queue with vertices
bool m_IsClosed = false;
};
} // namespace mitk
#endif
diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h
index e1b5125feb..2ebfaabffe 100644
--- a/Modules/ContourModel/DataManagement/mitkContourModel.h
+++ b/Modules/ContourModel/DataManagement/mitkContourModel.h
@@ -1,489 +1,489 @@
/*============================================================================
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 mitkContourModel_h
#define mitkContourModel_h
#include "mitkBaseData.h"
#include "mitkCommon.h"
#include <MitkContourModelExports.h>
#include <mitkContourElement.h>
namespace mitk
{
/**
\brief ContourModel is a structure of linked vertices defining a contour in 3D space.
The vertices are stored in a mitk::ContourElement for each timestep.
The contour line segments are implicitly defined by the given linked vertices.
By default two control points are linked by a straight line. It is possible to add
vertices at the front and end of the contour and to iterate in both directions.
Points are specified containing coordinates and additional (data) information,
see mitk::ContourElement.
For accessing a specific vertex either an index or a position in 3D space can be used.
The vertices are best accessed by using a VertexIterator.
Interaction with the contour is thus available without any mitk interactor class using the
api of ContourModel. It is possible to shift single vertices as well as shifting the whole
contour.
A contour can be either open like a single curved line segment or
closed. A closed contour can for example represent a jordan curve.
\section mitkContourModelDisplayOptions Display Options
The default mappers for this data structure are mitk::ContourModelGLMapper2D and
mitk::ContourModelMapper3D. See these classes for display options which can
can be set via properties.
*/
class MITKCONTOURMODEL_EXPORT ContourModel : public BaseData
{
public:
mitkClassMacro(ContourModel, BaseData);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/
typedef ContourElement::VertexType VertexType;
typedef ContourElement::VertexListType VertexListType;
typedef ContourElement::VertexIterator VertexIterator;
typedef ContourElement::ConstVertexIterator ConstVertexIterator;
typedef std::vector<ContourElement::Pointer> ContourModelSeries;
/*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/
/** \brief Possible interpolation of the line segments between control points */
enum LineSegmentInterpolation
{
LINEAR,
B_SPLINE
};
/*++++++++++++++++ inline methods +++++++++++++++++++++++*/
/** \brief Get the current selected vertex.
*/
VertexType *GetSelectedVertex() { return this->m_SelectedVertex; }
/** \brief Deselect vertex.
*/
void Deselect() { this->m_SelectedVertex = nullptr; }
/** \brief Set selected vertex as control point
*/
void SetSelectedVertexAsControlPoint(bool isControlPoint = true)
{
if (this->m_SelectedVertex)
{
m_SelectedVertex->IsControlPoint = isControlPoint;
this->Modified();
}
}
/** \brief Set the interpolation of the line segments between control points.
*/
void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation)
{
this->m_lineInterpolation = interpolation;
this->Modified();
}
/** \brief Get the interpolation of the line segments between control points.
*/
LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; }
/*++++++++++++++++ END inline methods +++++++++++++++++++++++*/
/** \brief Add a vertex to the contour at given timestep.
The vertex is added at the end of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertex(const Point3D &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour at given timestep.
A copy of the passed vertex is added at the end of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertex(const VertexType &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
\param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points
will be rendered).
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertex(const Point3D& vertex, bool isControlPoint, TimeStepType timestep = 0);
/** Clears the contour of destinationTimeStep and copies
the contour of the passed source model at the sourceTimeStep.
- @pre soureModel must point to a valid instance
+ @pre sourceModel must point to a valid instance
@pre sourceTimePoint must be valid
@note Updating a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep);
/** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour.
The vertex is added at the FRONT of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertexAtFront(const Point3D &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour.
The vertex is added at the FRONT of contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertexAtFront(const VertexType &vertex, TimeStepType timestep = 0);
/** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour.
\param vertex - coordinate representation of a control point
\param timestep - the timestep at which the vertex will be add ( default 0)
\param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points
will be rendered).
@note Adding a vertex to a timestep which exceeds the timebounds of the contour
will not be added, the TimeGeometry will not be expanded.
*/
void AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0);
/** \brief Insert a vertex at given index.
*/
void InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint = false, TimeStepType timestep = 0);
/** \brief Set a coordinates for point at given index.
*/
bool SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep = 0);
/** \brief Set a coordinates and control state for point at given index.
*/
bool SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep = 0);
/** \brief Return if the contour is closed or not.
*/
bool IsClosed(int timestep = 0) const;
/** \brief Concatenate two contours.
The starting control point of the other will be added at the end of the contour.
\param other
\param timestep - the timestep at which the vertex will be add ( default 0)
\param check - check for intersections ( default false)
*/
void Concatenate(ContourModel *other, TimeStepType timestep = 0, bool check = false);
/** \brief Returns a const VertexIterator at the start element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator Begin(TimeStepType timestep = 0) const;
/** \brief Returns a const VertexIterator at the start element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator IteratorBegin(TimeStepType timestep = 0) const;
/** \brief Returns a const VertexIterator at the end element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator End(TimeStepType timestep = 0) const;
/** \brief Returns a const VertexIterator at the end element of the contour.
@throw mitk::Exception if the timestep is invalid.
*/
VertexIterator IteratorEnd(TimeStepType timestep = 0) const;
/** \brief Close the contour.
The last control point will be linked with the first point.
*/
virtual void Close(TimeStepType timestep = 0);
/** \brief Set isClosed to false contour.
The link between the last control point the first point will be removed.
*/
virtual void Open(TimeStepType timestep = 0);
/** \brief Set closed property to given boolean.
false - The link between the last control point the first point will be removed.
true - The last control point will be linked with the first point.
*/
virtual void SetClosed(bool isClosed, TimeStepType timestep = 0);
/** \brief Returns the number of vertices at a given timestep.
\param timestep - default = 0
*/
int GetNumberOfVertices(TimeStepType timestep = 0) const;
/** \brief Returns whether the contour model is empty at a given timestep.
\param timestep - default = 0
*/
virtual bool IsEmpty(TimeStepType timestep) const;
/** \brief Returns whether the contour model is empty.
*/
bool IsEmpty() const override;
/** \brief Returns the vertex at the index position within the container.
* If the index or timestep is invalid a nullptr will be returned.
*/
virtual const VertexType *GetVertexAt(int index, TimeStepType timestep = 0) const;
const VertexType *GetVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const;
/** Returns the next control vertex to the approximate nearest vertex of a given position in 3D space
* If the timestep is invalid a nullptr will be returned.
*/
virtual const VertexType *GetNextControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const;
/** Returns the previous control vertex to the approximate nearest vertex of a given position in 3D space
* If the timestep is invalid a nullptr will be returned.
*/
virtual const VertexType *GetPreviousControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const;
/** \brief Remove a vertex at given timestep within the container.
\return index of vertex. -1 if not found.
*/
int GetIndex(const VertexType *vertex, TimeStepType timestep = 0);
/** \brief Check if there isn't something at this timestep.
*/
bool IsEmptyTimeStep(unsigned int t) const override;
/** \brief Check if mouse cursor is near the contour.
*/
bool IsNearContour(Point3D &point, float eps, TimeStepType timestep) const;
/** Function that searches for the line segment of the contour that is closest to the passed point
and close enough (distance between point and line segment <= eps). If such an line segment exist,
the starting vertex and closing vertex of the found segment are passed back.
@return True indicates that a line segment was found. False indicates that no segment of the contour
is close enough to the passed point.
@remark previousVertex and nextVertex are only valid if return is true.*/
bool GetLineSegmentForPoint(Point3D &point,
float eps,
TimeStepType timestep,
mitk::ContourElement::VertexType *previousVertex = nullptr,
mitk::ContourElement::VertexType *nextVertex = nullptr);
/**Overloaded version that returns additional information (start and end vertix of the line
closest to the passed point and the closest point on the contour).
@remark segmentStart, segmentStop and closestContourPoint are only valid if the function returns true.
*/
bool GetLineSegmentForPoint(const mitk::Point3D& point,
float eps, TimeStepType timestep, ContourElement::VertexSizeType& segmentStartIndex,
ContourElement::VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint,
bool findClosest = true) const;
/** \brief Mark a vertex at an index in the container as selected.
*/
bool SelectVertexAt(int index, TimeStepType timestep = 0);
/** \brief Mark a vertex at an index in the container as control point.
*/
bool SetControlVertexAt(int index, TimeStepType timestep = 0);
/** \brief Mark a control vertex at a given position in 3D space.
\param point - query point in 3D space
\param eps - radius for nearest neighbour search (error bound).
\param timestep - search at this timestep
@return true = vertex found; false = no vertex found
*/
bool SelectControlVertexAt(const Point3D &point, float eps, TimeStepType timestep = 0);
/** \brief Mark a vertex at a given position in 3D space.
\param point - query point in 3D space
\param eps - radius for nearest neighbour search (error bound).
\param timestep - search at this timestep
@return true = vertex found; false = no vertex found
*/
bool SelectVertexAt(const Point3D &point, float eps, TimeStepType timestep = 0);
/*
- \pararm point - query point in 3D space
- \pararm eps - radius for nearest neighbour search (error bound).
- \pararm timestep - search at this timestep
+ \param point - query point in 3D space
+ \param eps - radius for nearest neighbour search (error bound).
+ \param timestep - search at this timestep
@return true = vertex found; false = no vertex found
*/
bool SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0);
/** \brief Remove a vertex at given index within the container.
@return true = the vertex was successfuly removed; false = wrong index.
*/
bool RemoveVertexAt(int index, TimeStepType timestep = 0);
/** \brief Remove a vertex at given timestep within the container.
@return true = the vertex was successfuly removed.
*/
bool RemoveVertex(const VertexType *vertex, TimeStepType timestep = 0);
/** \brief Remove a vertex at a query position in 3D space.
The vertex to be removed will be search by nearest neighbour search.
Note that possibly no vertex at this position and eps is stored inside
the contour.
@return true = the vertex was successfuly removed; false = no vertex found.
*/
bool RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep = 0);
/** \brief Shift the currently selected vertex by a translation vector.
\param translate - the translation vector.
*/
void ShiftSelectedVertex(Vector3D &translate);
/** \brief Shift the whole contour by a translation vector at given timestep.
\param translate - the translation vector.
\param timestep - at this timestep the contour will be shifted.
*/
void ShiftContour(Vector3D &translate, TimeStepType timestep = 0);
/** \brief Clear the storage container at given timestep.
All control points are removed at
timestep.
*/
virtual void Clear(TimeStepType timestep);
/** \brief Initialize all data objects
*/
void Initialize() override;
/** \brief Initialize object with specs of other contour.
Note: No data will be copied.
*/
void Initialize(const ContourModel &other);
/** \brief Returns a list pointing to all vertices that are indicated to be control points.
*/
VertexListType GetControlVertices(TimeStepType timestep);
/** \brief Returns the container of the vertices.
*/
VertexListType GetVertexList(TimeStepType timestep);
/*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
void SetRequestedRegionToLargestPossibleRegion() override;
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
bool VerifyRequestedRegion() override;
/**
\brief Inherit from base data - no region support available for contourModel objects.
*/
void SetRequestedRegion(const itk::DataObject *data) override;
/**
\brief Expand the contour model and its TimeGeometry to given number of timesteps.
*/
void Expand(unsigned int timeSteps) override;
/**
\brief Update the OutputInformation of a ContourModel object
The BoundingBox of the contour will be updated, if necessary.
*/
void UpdateOutputInformation() override;
/**
\brief Clear the storage container.
The object is set to initial state. All control points are removed and the number of
timesteps are set to 1.
*/
void Clear() override;
/**
\brief overwrite if the Data can be called by an Interactor (StateMachine).
*/
void ExecuteOperation(Operation *operation) override;
- /** \brief Redistributes ontrol vertices with a given period (as number of vertices)
+ /** \brief Redistributes control vertices with a given period (as number of vertices)
\param period - the number of vertices between control points.
\param timestep - at this timestep all lines will be rebuilt.
*/
virtual void RedistributeControlVertices(int period, TimeStepType timestep);
protected:
mitkCloneMacro(Self);
ContourModel();
ContourModel(const ContourModel &other);
~ContourModel() override;
// inherit from BaseData. called by Clear()
void ClearData() override;
// inherit from BaseData. Initial state of a contour with no vertices and a single timestep.
void InitializeEmpty() override;
// Shift a vertex
static void ShiftVertex(VertexType *vertex, Vector3D &vector);
// Storage with time resolved support.
ContourModelSeries m_ContourSeries;
// The currently selected vertex.
VertexType *m_SelectedVertex;
// The interpolation of the line segment between control points.
LineSegmentInterpolation m_lineInterpolation;
// only update the bounding geometry if necessary
bool m_UpdateBoundingBox;
};
itkEventMacroDeclaration(ContourModelEvent, itk::AnyEvent);
itkEventMacroDeclaration(ContourModelShiftEvent, ContourModelEvent);
itkEventMacroDeclaration(ContourModelSizeChangeEvent, ContourModelEvent);
itkEventMacroDeclaration(ContourModelAddEvent, ContourModelSizeChangeEvent);
itkEventMacroDeclaration(ContourModelRemoveEvent, ContourModelSizeChangeEvent);
itkEventMacroDeclaration(ContourModelExpandTimeBoundsEvent, ContourModelEvent);
itkEventMacroDeclaration(ContourModelClosedEvent, ContourModelEvent);
}
#endif
diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h
index c27194620a..9ef7e1bd39 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h
+++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h
@@ -1,67 +1,67 @@
/*============================================================================
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 mitkContourModelGLMapper2D_h
#define mitkContourModelGLMapper2D_h
#include "mitkCommon.h"
#include "mitkContourModel.h"
#include "mitkContourModelGLMapper2DBase.h"
#include <MitkContourModelExports.h>
namespace mitk
{
class BaseRenderer;
class ContourModel;
/**
* @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window
*
*
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelGLMapper2D : public ContourModelGLMapper2DBase
{
public:
mitkClassMacro(ContourModelGLMapper2D, ContourModelGLMapper2DBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* reimplemented from Baseclass
*/
void MitkRender(BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override;
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
LocalStorageHandler<BaseLocalStorage> m_LSH;
protected:
ContourModelGLMapper2D();
~ContourModelGLMapper2D() override;
mitk::ContourModel::Pointer m_SubdivisionContour;
bool m_InitSubdivisionCurve;
private:
/**
- * return a refernce of the rendered data object
+ * return a reference of the rendered data object
*/
ContourModel *GetInput(void);
};
} // namespace mitk
#endif
diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp
index 1dcf695ea1..dcc46349ee 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp
+++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp
@@ -1,386 +1,386 @@
/*============================================================================
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 "mitkContourModelSetGLMapper2D.h"
#include "mitkColorProperty.h"
#include "mitkContourModelSet.h"
#include "mitkPlaneGeometry.h"
#include "mitkProperties.h"
#include <vtkContext2D.h>
#include <vtkLinearTransform.h>
#include <vtkOpenGLContextDevice2D.h>
#include <vtkPen.h>
#include "mitkManualPlacementAnnotationRenderer.h"
#include "mitkBaseRenderer.h"
#include "mitkContourModel.h"
#include "mitkTextAnnotation2D.h"
mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase()
{
m_PointNumbersAnnotation = mitk::TextAnnotation2D::New();
m_ControlPointNumbersAnnotation = mitk::TextAnnotation2D::New();
}
mitk::ContourModelGLMapper2DBase::~ContourModelGLMapper2DBase()
{
}
void mitk::ContourModelGLMapper2DBase::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/)
{
auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
// check for color prop and use it for rendering if it exists
GetDataNode()->GetColor(rgba, renderer, "color");
// check for opacity prop and use it for rendering if it exists
GetDataNode()->GetOpacity(rgba[3], renderer, "opacity");
if (localStorage->Context->GetPen() == nullptr)
{
return;
}
localStorage->Context->GetPen()->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]);
}
void mitk::ContourModelGLMapper2DBase::DrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer)
{
if (std::find(m_RendererList.begin(), m_RendererList.end(), renderer) == m_RendererList.end())
{
m_RendererList.push_back(renderer);
}
mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_PointNumbersAnnotation.GetPointer(), renderer);
m_PointNumbersAnnotation->SetVisibility(false);
mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_ControlPointNumbersAnnotation.GetPointer(), renderer);
m_ControlPointNumbersAnnotation->SetVisibility(false);
InternalDrawContour(renderingContour, renderer);
}
void mitk::ContourModelGLMapper2DBase::InternalDrawContour(mitk::ContourModel *renderingContour,
mitk::BaseRenderer *renderer)
{
if (!renderingContour)
return;
auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
localStorage->Device = vtkSmartPointer<vtkOpenGLContextDevice2D>::New();
localStorage->Context = vtkSmartPointer<vtkContext2D>::New();
localStorage->Device->Begin(renderer->GetVtkRenderer());
localStorage->Context->Begin(localStorage->Device);
mitk::DataNode *dataNode = this->GetDataNode();
renderingContour->UpdateOutputInformation();
const auto timestep = this->GetTimestep();
if (!renderingContour->IsEmptyTimeStep(timestep))
{
// apply color and opacity read from the PropertyList
ApplyColorAndOpacityProperties(renderer);
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(dataNode->GetProperty("contour.color", renderer));
float opacity = 0.5;
dataNode->GetFloatProperty("opacity", opacity, renderer);
if (colorprop)
{
// set the color of the contour
double red = colorprop->GetColor().GetRed();
double green = colorprop->GetColor().GetGreen();
double blue = colorprop->GetColor().GetBlue();
localStorage->Context->GetPen()->SetColorF(red, green, blue, opacity);
}
mitk::ColorProperty::Pointer selectedcolor =
dynamic_cast<mitk::ColorProperty *>(dataNode->GetProperty("contour.points.color", renderer));
if (!selectedcolor)
{
selectedcolor = mitk::ColorProperty::New(1.0, 0.0, 0.1);
}
vtkLinearTransform *transform = dataNode->GetVtkTransform();
// ContourModel::OutputType point;
mitk::Point3D point;
mitk::Point3D p;
float vtkp[3];
float lineWidth = 3.0;
bool drawit = false;
bool isHovering = false;
dataNode->GetBoolProperty("contour.hovering", isHovering);
if (isHovering)
dataNode->GetFloatProperty("contour.hovering.width", lineWidth);
else
dataNode->GetFloatProperty("contour.width", lineWidth);
bool showSegments = false;
dataNode->GetBoolProperty("contour.segments.show", showSegments);
bool showControlPoints = false;
dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints);
bool showPoints = false;
dataNode->GetBoolProperty("contour.points.show", showPoints);
bool showPointsNumbers = false;
dataNode->GetBoolProperty("contour.points.text", showPointsNumbers);
bool showControlPointsNumbers = false;
dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers);
bool projectmode = false;
dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane");
auto pointsIt = renderingContour->IteratorBegin(timestep);
Point2D pt2d; // projected_p in display coordinates
Point2D lastPt2d;
int index = 0;
mitk::ScalarType maxDiff = 0.25;
while (pointsIt != renderingContour->IteratorEnd(timestep))
{
lastPt2d = pt2d;
point = (*pointsIt)->Coordinates;
itk2vtk(point, vtkp);
transform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, p);
renderer->WorldToView(p, pt2d);
ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p));
// project to plane
if (projectmode)
{
drawit = true;
}
else if (scalardiff < maxDiff) // point is close enough to be drawn
{
drawit = true;
}
else
{
drawit = false;
}
// draw line
if (drawit)
{
if (showSegments)
{
// lastPt2d is not valid in first step
if (!(pointsIt == renderingContour->IteratorBegin(timestep)))
{
localStorage->Context->GetPen()->SetWidth(lineWidth);
localStorage->Context->DrawLine(pt2d[0], pt2d[1], lastPt2d[0], lastPt2d[1]);
localStorage->Context->GetPen()->SetWidth(1);
}
}
if (showControlPoints)
{
- // draw ontrol points
+ // draw control points
if ((*pointsIt)->IsControlPoint)
{
float pointsize = 4;
Point2D tmp;
Vector2D horz, vert;
horz[1] = 0;
vert[0] = 0;
horz[0] = pointsize;
vert[1] = pointsize;
localStorage->Context->GetPen()->SetColorF(selectedcolor->GetColor().GetRed(),
selectedcolor->GetColor().GetBlue(),
selectedcolor->GetColor().GetGreen());
localStorage->Context->GetPen()->SetWidth(1);
// a rectangle around the point with the selected color
auto* rectPts = new float[8];
tmp = pt2d - horz;
rectPts[0] = tmp[0];
rectPts[1] = tmp[1];
tmp = pt2d + vert;
rectPts[2] = tmp[0];
rectPts[3] = tmp[1];
tmp = pt2d + horz;
rectPts[4] = tmp[0];
rectPts[5] = tmp[1];
tmp = pt2d - vert;
rectPts[6] = tmp[0];
rectPts[7] = tmp[1];
localStorage->Context->DrawPolygon(rectPts,4);
// the actual point in the specified color to see the usual color of the point
localStorage->Context->GetPen()->SetColorF(
colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue());
localStorage->Context->DrawPoint(pt2d[0], pt2d[1]);
}
}
if (showPoints)
{
float pointsize = 3;
Point2D tmp;
Vector2D horz, vert;
horz[1] = 0;
vert[0] = 0;
horz[0] = pointsize;
vert[1] = pointsize;
localStorage->Context->GetPen()->SetColorF(0.0, 0.0, 0.0);
localStorage->Context->GetPen()->SetWidth(1);
// a rectangle around the point with the selected color
auto* rectPts = new float[8];
tmp = pt2d - horz;
rectPts[0] = tmp[0];
rectPts[1] = tmp[1];
tmp = pt2d + vert;
rectPts[2] = tmp[0];
rectPts[3] = tmp[1];
tmp = pt2d + horz;
rectPts[4] = tmp[0];
rectPts[5] = tmp[1];
tmp = pt2d - vert;
rectPts[6] = tmp[0];
rectPts[7] = tmp[1];
localStorage->Context->DrawPolygon(rectPts, 4);
// the actual point in the specified color to see the usual color of the point
localStorage->Context->GetPen()->SetColorF(
colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue());
localStorage->Context->DrawPoint(pt2d[0], pt2d[1]);
}
if (showPointsNumbers)
{
std::string l;
std::stringstream ss;
ss << index;
l.append(ss.str());
float rgb[3];
rgb[0] = 0.0;
rgb[1] = 0.0;
rgb[2] = 0.0;
WriteTextWithAnnotation(m_PointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer);
}
if (showControlPointsNumbers && (*pointsIt)->IsControlPoint)
{
std::string l;
std::stringstream ss;
ss << index;
l.append(ss.str());
float rgb[3];
rgb[0] = 1.0;
rgb[1] = 1.0;
rgb[2] = 0.0;
WriteTextWithAnnotation(m_ControlPointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer);
}
index++;
}
pointsIt++;
} // end while iterate over controlpoints
// close contour if necessary
if (renderingContour->IsClosed(timestep) && drawit && showSegments)
{
lastPt2d = pt2d;
point = renderingContour->GetVertexAt(0, timestep)->Coordinates;
itk2vtk(point, vtkp);
transform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, p);
renderer->WorldToDisplay(p, pt2d);
localStorage->Context->GetPen()->SetWidth(lineWidth);
localStorage->Context->DrawLine(lastPt2d[0], lastPt2d[1], pt2d[0], pt2d[1]);
localStorage->Context->GetPen()->SetWidth(1);
}
// draw selected vertex if exists
if (renderingContour->GetSelectedVertex())
{
// transform selected vertex
point = renderingContour->GetSelectedVertex()->Coordinates;
itk2vtk(point, vtkp);
transform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, p);
renderer->WorldToDisplay(p, pt2d);
ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p));
//----------------------------------
// draw point if close to plane
if (scalardiff < maxDiff)
{
float pointsize = 5;
Point2D tmp;
localStorage->Context->GetPen()->SetColorF(0.0, 1.0, 0.0);
localStorage->Context->GetPen()->SetWidth(1);
// a rectangle around the point with the selected color
auto* rectPts = new float[8];
// a diamond around the point
// begin from upper left corner and paint clockwise
rectPts[0] = pt2d[0] - pointsize;
rectPts[1] = pt2d[1] + pointsize;
rectPts[2] = pt2d[0] + pointsize;
rectPts[3] = pt2d[1] + pointsize;
rectPts[4] = pt2d[0] + pointsize;
rectPts[5] = pt2d[1] - pointsize;
rectPts[6] = pt2d[0] - pointsize;
rectPts[7] = pt2d[1] - pointsize;
localStorage->Context->DrawPolygon(rectPts, 4);
}
//------------------------------------
}
}
localStorage->Context = nullptr;
localStorage->Device = nullptr;
}
void mitk::ContourModelGLMapper2DBase::WriteTextWithAnnotation(TextAnnotationPointerType textAnnotation,
const char *text,
float rgb[3],
Point2D /*pt2d*/,
mitk::BaseRenderer * /*renderer*/)
{
textAnnotation->SetText(text);
textAnnotation->SetColor(rgb);
textAnnotation->SetOpacity(1);
textAnnotation->SetFontSize(16);
textAnnotation->SetBoolProperty("drawShadow", false);
textAnnotation->SetVisibility(true);
}
diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp
index 4fdf5d812d..01e848e008 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp
+++ b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp
@@ -1,372 +1,372 @@
/*============================================================================
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 <mitkContourModelMapper2D.h>
#include <mitkContourModelSubDivisionFilter.h>
#include <vtkAppendPolyData.h>
#include <vtkCellArray.h>
#include <vtkCutter.h>
#include <vtkPlane.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
#include <vtkSphereSource.h>
#include <vtkStripper.h>
#include <vtkTubeFilter.h>
#include <mitkPlaneGeometry.h>
mitk::ContourModelMapper2D::ContourModelMapper2D()
{
}
mitk::ContourModelMapper2D::~ContourModelMapper2D()
{
}
const mitk::ContourModel *mitk::ContourModelMapper2D::GetInput(void)
{
- // convient way to get the data from the dataNode
+ // convenient way to get the data from the dataNode
return static_cast<const mitk::ContourModel *>(GetDataNode()->GetData());
}
vtkProp *mitk::ContourModelMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actor;
}
void mitk::ContourModelMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
/*++ convert the contour to vtkPolyData and set it as input for our mapper ++*/
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
auto *inputContour = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
const auto timestep = this->GetTimestep();
// if there's something to be rendered
if (inputContour->GetNumberOfVertices(timestep) > 0)
{
localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour, renderer);
}
this->ApplyContourProperties(renderer);
localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData);
}
void mitk::ContourModelMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
// check if there is something to be rendered
auto *data = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
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(renderer->GetTimeStep())))
{
// clear the rendered polydata
localStorage->m_Mapper->RemoveAllInputs(); // SetInput(vtkSmartPointer<vtkPolyData>::New());
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()) // was the node modified?
||
(localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified?
||
(localStorage->m_LastUpdateTime <
renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified?
||
(localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified?
||
(localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->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();
}
vtkSmartPointer<vtkPolyData> mitk::ContourModelMapper2D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour,
mitk::BaseRenderer *renderer)
{
const auto timestep = this->GetTimestep();
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> resultingPolyData = vtkSmartPointer<vtkPolyData>::New();
// check for the worldgeometry from the current render window
const mitk::PlaneGeometry *currentWorldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (currentWorldGeometry)
{
// origin and normal of vtkPlane
mitk::Point3D origin = currentWorldGeometry->GetOrigin();
mitk::Vector3D normal = currentWorldGeometry->GetNormal();
// the implicit function to slice through the polyData
vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
plane->SetOrigin(origin[0], origin[1], origin[2]);
plane->SetNormal(normal[0], normal[1], normal[2]);
/* First of all convert the control points of the contourModel to vtk points
* and add lines in between them
*/
// the points to draw
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// the lines to connect the points
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyDataIn3D = vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer<vtkAppendPolyData> appendPoly = vtkSmartPointer<vtkAppendPolyData>::New();
mitk::ContourModel::Pointer renderingContour = mitk::ContourModel::New();
renderingContour = inputContour;
bool subdivision = false;
this->GetDataNode()->GetBoolProperty("subdivision curve", subdivision, renderer);
if (subdivision)
{
mitk::ContourModel::Pointer subdivContour = mitk::ContourModel::New();
mitk::ContourModelSubDivisionFilter::Pointer subdivFilter = mitk::ContourModelSubDivisionFilter::New();
subdivFilter->SetInput(inputContour);
subdivFilter->Update();
subdivContour = subdivFilter->GetOutput();
if (subdivContour->GetNumberOfVertices() == 0)
{
subdivContour = inputContour;
}
renderingContour = subdivContour;
}
// iterate over all control points
auto current = renderingContour->IteratorBegin(timestep);
auto next = renderingContour->IteratorBegin(timestep);
if (next != renderingContour->IteratorEnd(timestep))
{
next++;
auto end = renderingContour->IteratorEnd(timestep);
while (next != end)
{
mitk::ContourModel::VertexType *currentControlPoint = *current;
mitk::ContourModel::VertexType *nextControlPoint = *next;
vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0],
currentControlPoint->Coordinates[1],
currentControlPoint->Coordinates[2]);
vtkIdType p2 = points->InsertNextPoint(
nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]);
// add the line between both contorlPoints
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
if (currentControlPoint->IsControlPoint)
{
double coordinates[3];
coordinates[0] = currentControlPoint->Coordinates[0];
coordinates[1] = currentControlPoint->Coordinates[1];
coordinates[2] = currentControlPoint->Coordinates[2];
double distance = plane->DistanceToPlane(coordinates);
if (distance < 0.1)
{
vtkSmartPointer<vtkSphereSource> sphere = vtkSmartPointer<vtkSphereSource>::New();
sphere->SetRadius(1.2);
sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]);
sphere->Update();
appendPoly->AddInputConnection(sphere->GetOutputPort());
}
}
current++;
next++;
} // end while (it!=end)
// check if last control point is enabled to draw it
if ((*current)->IsControlPoint)
{
double coordinates[3];
coordinates[0] = (*current)->Coordinates[0];
coordinates[1] = (*current)->Coordinates[1];
coordinates[2] = (*current)->Coordinates[2];
double distance = plane->DistanceToPlane(coordinates);
if (distance < 0.1)
{
vtkSmartPointer<vtkSphereSource> sphere = vtkSmartPointer<vtkSphereSource>::New();
sphere->SetRadius(1.2);
sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]);
sphere->Update();
appendPoly->AddInputConnection(sphere->GetOutputPort());
}
}
/* If the contour is closed an additional line has to be created between the very first point
* and the last point
*/
if (renderingContour->IsClosed(timestep))
{
// add a line from the last to the first control point
mitk::ContourModel::VertexType *firstControlPoint = *(renderingContour->IteratorBegin(timestep));
mitk::ContourModel::VertexType *lastControlPoint = *(--(renderingContour->IteratorEnd(timestep)));
vtkIdType p2 = points->InsertNextPoint(
lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]);
vtkIdType p1 = points->InsertNextPoint(
firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]);
// add the line between both contorlPoints
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
} // end if(isClosed)
// Add the points to the dataset
polyDataIn3D->SetPoints(points);
// Add the lines to the dataset
polyDataIn3D->SetLines(lines);
// cut through polyData
bool useCuttingPlane = false;
this->GetDataNode()->GetBoolProperty("use cutting plane", useCuttingPlane, renderer);
if (useCuttingPlane)
{
// slice through the data to get a 2D representation of the (possible) 3D contour
- // needed because currently there is no outher solution if the contour is within the plane
+ // needed because currently there is no other solution if the contour is within the plane
vtkSmartPointer<vtkTubeFilter> tubeFilter = vtkSmartPointer<vtkTubeFilter>::New();
tubeFilter->SetInputData(polyDataIn3D);
tubeFilter->SetRadius(0.05);
// cuts through vtkPolyData with a given implicit function. In our case a plane
vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
cutter->SetCutFunction(plane);
cutter->SetInputConnection(tubeFilter->GetOutputPort());
// we want the scalars of the input - so turn off generating the scalars within vtkCutter
cutter->GenerateCutScalarsOff();
cutter->Update();
// set to 2D representation of the contour
resultingPolyData = cutter->GetOutput();
} // end if(project contour)
else
{
// set to 3D polyData
resultingPolyData = polyDataIn3D;
}
} // end if (it != end)
appendPoly->AddInputData(resultingPolyData);
appendPoly->Update();
// return contour with control points
return appendPoly->GetOutput();
}
else
{
// return empty polyData
return resultingPolyData;
}
}
void mitk::ContourModelMapper2D::ApplyContourProperties(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
float lineWidth(1.0);
if (this->GetDataNode()->GetFloatProperty("width", lineWidth, renderer))
{
localStorage->m_Actor->GetProperty()->SetLineWidth(lineWidth);
}
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("color", renderer));
if (colorprop)
{
// set the color of the contour
double red = colorprop->GetColor().GetRed();
double green = colorprop->GetColor().GetGreen();
double blue = colorprop->GetColor().GetBlue();
localStorage->m_Actor->GetProperty()->SetColor(red, green, blue);
}
// make sure that directional lighting isn't used for our contour
localStorage->m_Actor->GetProperty()->SetAmbient(1.0);
localStorage->m_Actor->GetProperty()->SetDiffuse(0.0);
localStorage->m_Actor->GetProperty()->SetSpecular(0.0);
}
/*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/
mitk::ContourModelMapper2D::LocalStorage *mitk::ContourModelMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
mitk::ContourModelMapper2D::LocalStorage::LocalStorage()
{
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
// set the mapper for the actor
m_Actor->SetMapper(m_Mapper);
}
void mitk::ContourModelMapper2D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
node->AddProperty("color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite);
node->AddProperty("width", mitk::FloatProperty::New(1.0), renderer, overwrite);
node->AddProperty("use cutting plane", mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty("subdivision curve", mitk::BoolProperty::New(false), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp
index e6b499da4b..74cd97e436 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp
+++ b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp
@@ -1,233 +1,233 @@
/*============================================================================
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 <mitkContourModelMapper3D.h>
#include <vtkCellArray.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
mitk::ContourModelMapper3D::ContourModelMapper3D()
{
}
mitk::ContourModelMapper3D::~ContourModelMapper3D()
{
}
const mitk::ContourModel *mitk::ContourModelMapper3D::GetInput(void)
{
- // convient way to get the data from the dataNode
+ // convenient way to get the data from the dataNode
return static_cast<const mitk::ContourModel *>(GetDataNode()->GetData());
}
vtkProp *mitk::ContourModelMapper3D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actor;
}
void mitk::ContourModelMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
/* First convert the contourModel to vtkPolyData, then tube filter it and
* set it input for our mapper
*/
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
auto *inputContour = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour);
this->ApplyContourProperties(renderer);
// tube filter the polyData
localStorage->m_TubeFilter->SetInputData(localStorage->m_OutlinePolyData);
float lineWidth(1.0);
if (this->GetDataNode()->GetFloatProperty("contour.3D.width", lineWidth, renderer))
{
localStorage->m_TubeFilter->SetRadius(lineWidth);
}
else
{
localStorage->m_TubeFilter->SetRadius(0.5);
}
localStorage->m_TubeFilter->CappingOn();
localStorage->m_TubeFilter->SetNumberOfSides(10);
localStorage->m_TubeFilter->Update();
localStorage->m_Mapper->SetInputConnection(localStorage->m_TubeFilter->GetOutputPort());
}
void mitk::ContourModelMapper3D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
auto *data = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
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->IsValidTimePoint(renderer->GetTime())) || (this->GetTimestep() == TIMESTEP_INVALID))
{
// clear the rendered polydata
localStorage->m_Mapper->SetInputData(vtkSmartPointer<vtkPolyData>::New());
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()) // was the node modified?
||
(localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified?
||
(localStorage->m_LastUpdateTime <
renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified?
||
(localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified?
||
(localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->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();
}
vtkSmartPointer<vtkPolyData> mitk::ContourModelMapper3D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour)
{
const auto timestep = this->GetTimestep();
// the points to draw
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// the lines to connect the points
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// iterate over the control points
auto current = inputContour->IteratorBegin(timestep);
auto next = inputContour->IteratorBegin(timestep);
if (next != inputContour->IteratorEnd(timestep))
{
next++;
auto end = inputContour->IteratorEnd(timestep);
while (next != end)
{
mitk::ContourModel::VertexType *currentControlPoint = *current;
mitk::ContourModel::VertexType *nextControlPoint = *next;
if (!(currentControlPoint->Coordinates[0] == nextControlPoint->Coordinates[0] &&
currentControlPoint->Coordinates[1] == nextControlPoint->Coordinates[1] &&
currentControlPoint->Coordinates[2] == nextControlPoint->Coordinates[2]))
{
vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0],
currentControlPoint->Coordinates[1],
currentControlPoint->Coordinates[2]);
vtkIdType p2 = points->InsertNextPoint(
nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]);
// add the line between both contorlPoints
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
current++;
next++;
}
if (inputContour->IsClosed(timestep))
{
// If the contour is closed add a line from the last to the first control point
mitk::ContourModel::VertexType *firstControlPoint = *(inputContour->IteratorBegin(timestep));
mitk::ContourModel::VertexType *lastControlPoint = *(--(inputContour->IteratorEnd(timestep)));
if (lastControlPoint->Coordinates[0] != firstControlPoint->Coordinates[0] ||
lastControlPoint->Coordinates[1] != firstControlPoint->Coordinates[1] ||
lastControlPoint->Coordinates[2] != firstControlPoint->Coordinates[2])
{
vtkIdType p2 = points->InsertNextPoint(
lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]);
vtkIdType p1 = points->InsertNextPoint(
firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]);
// add the line to the cellArray
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
}
// Add the points to the dataset
polyData->SetPoints(points);
// Add the lines to the dataset
polyData->SetLines(lines);
}
return polyData;
}
void mitk::ContourModelMapper3D::ApplyContourProperties(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("contour.color", renderer));
if (colorprop)
{
// set the color of the contour
double red = colorprop->GetColor().GetRed();
double green = colorprop->GetColor().GetGreen();
double blue = colorprop->GetColor().GetBlue();
localStorage->m_Actor->GetProperty()->SetColor(red, green, blue);
}
}
/*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/
mitk::ContourModelMapper3D::LocalStorage *mitk::ContourModelMapper3D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
mitk::ContourModelMapper3D::LocalStorage::LocalStorage()
{
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_TubeFilter = vtkSmartPointer<vtkTubeFilter>::New();
// set the mapper for the actor
m_Actor->SetMapper(m_Mapper);
}
void mitk::ContourModelMapper3D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("contour.3D.width", mitk::FloatProperty::New(0.5), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
diff --git a/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp b/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp
index fc0e5c17f2..e0fdd370f5 100644
--- a/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp
+++ b/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp
@@ -1,63 +1,63 @@
/*============================================================================
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 <mitkContourModelSet.h>
#include <mitkTestingMacros.h>
static void TestAddVertex()
{
mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New();
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
contourSet->AddContourModel(contour);
MITK_TEST_CONDITION(contourSet->GetSize() > 0, "Add a contour, size increased");
}
static void TestRemoveContourAtIndex()
{
mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New();
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
contourSet->AddContourModel(contour);
contourSet->RemoveContourModelAt(0);
MITK_TEST_CONDITION(contourSet->GetSize() == 0, "removed contour by index");
contourSet->AddContourModel(contour);
contourSet->RemoveContourModel(contour);
MITK_TEST_CONDITION(contourSet->GetSize() == 0, "removed contour by object");
}
static void TestEmptyContour()
{
mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New();
- MITK_TEST_CONDITION(contourSet->Begin() == contourSet->End(), "test iterator of emtpy contour");
+ MITK_TEST_CONDITION(contourSet->Begin() == contourSet->End(), "test iterator of empty contour");
MITK_TEST_CONDITION(contourSet->GetSize() == 0, "test numberof vertices of empty contour");
}
int mitkContourModelSetTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkContourModelSetTest")
TestEmptyContour();
TestAddVertex();
TestRemoveContourAtIndex();
MITK_TEST_END()
}
diff --git a/Modules/ContourModel/Testing/mitkContourModelTest.cpp b/Modules/ContourModel/Testing/mitkContourModelTest.cpp
index 9464bdcb83..e0b029a155 100644
--- a/Modules/ContourModel/Testing/mitkContourModelTest.cpp
+++ b/Modules/ContourModel/Testing/mitkContourModelTest.cpp
@@ -1,483 +1,483 @@
/*============================================================================
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 <mitkContourModel.h>
#include <mitkTestingMacros.h>
// Add a vertex to the contour and see if size changed
static void TestAddVertex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 1, "Add a Vertex, size increased");
MITK_TEST_CONDITION(contour->GetVertexAt(0)->Coordinates == p, "Added vertex has the correct value.");
mitk::Point3D outOfTimeBoundPoint;
outOfTimeBoundPoint[0] = outOfTimeBoundPoint[1] = outOfTimeBoundPoint[2] = 1;
contour->AddVertex(outOfTimeBoundPoint, mitk::TimeStepType(1));
MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Add a vertex to an unsupported time step has not changed geometry.");
MITK_TEST_CONDITION(contour->IsEmptyTimeStep(1), "Add a vertex to an unsupported time step has not added an contour element.");
MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == -1, "Add a vertex to an unsupported time step has not added an contour element.");
contour->Expand(3);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 2;
mitk::Point3D p3;
p3[0] = p3[1] = p3[2] = 3;
contour->AddVertex(p2, mitk::TimeStepType(1));
contour->AddVertex(mitk::ContourModel::VertexType(p3), mitk::TimeStepType(1));
MITK_TEST_CONDITION(!contour->IsEmptyTimeStep(1), "Add a vertex to an unsupported time step has not added an contour element.");
MITK_TEST_CONDITION(contour->GetVertexAt(0,1)->Coordinates == p2, "Add a vertex to the 2nd time step (as Point).");
MITK_TEST_CONDITION(contour->GetVertexAt(1,1)->Coordinates == p3, "Add a vertex to the 2nd time step via overload (as vertex type).");
MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == 2, "Add a vertex to an unsupported time step has not added an contour element.");
}
// Select a vertex by index. successful if the selected vertex member of the contour is no longer set to null
static void TestSelectVertexAtIndex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
contour->SelectVertexAt(0);
MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at index");
}
// Select a vertex by worldposition. successful if the selected vertex member of the contour is no longer set to null
static void TestSelectVertexAtWorldposition()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
// same point is used here so the epsilon can be chosen very small
contour->SelectVertexAt(p, 0.01);
MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at position");
}
// Move a vertex by a translation vector
static void TestMoveSelectedVertex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
// Same point is used here so the epsilon can be chosen very small
contour->SelectVertexAt(p, 0.01);
mitk::Vector3D v;
v[0] = 1;
v[1] = 3;
v[2] = -1;
contour->ShiftSelectedVertex(v);
const mitk::ContourModel::VertexType *vertex = contour->GetSelectedVertex();
bool correctlyMoved = false;
correctlyMoved =
(vertex->Coordinates)[0] == (v[0]) && (vertex->Coordinates)[1] == (v[1]) && (vertex->Coordinates)[2] == (v[2]);
MITK_TEST_CONDITION(correctlyMoved, "Vertex has been moved");
}
// Test to move the whole contour
/*
static void TestMoveContour()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 0;
contour->AddVertex(p2);
mitk::Vector3D v;
v[0] = 1;
v[1] = 3;
v[2] = -1;
contour->ShiftContour(v);
mitk::ContourModel::VertexIterator it = contour->IteratorBegin();
mitk::ContourModel::VertexIterator end = contour->IteratorEnd();
bool correctlyMoved = false;
while(it != end)
{
correctlyMoved &= (*it)->Coordinates[0] == (v[0]) &&
(*it)->Coordinates[1] == (v[1]) &&
(*it)->Coordinates[2] == (v[2]);
}
MITK_TEST_CONDITION(correctlyMoved, "Contour has been moved");
}
*/
// Remove a vertex by index
static void TestRemoveVertexAtIndex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
contour->RemoveVertexAt(0);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex");
}
// Remove a vertex by position
static void TestRemoveVertexAtWorldPosition()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
contour->RemoveVertexAt(p, 0.01);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex");
}
// Check closeable contour
static void TestIsclosed()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
contour->Close();
MITK_TEST_CONDITION(contour->IsClosed(), "closed contour");
// no vertices should be added to a closed contour
int oldNumberOfVertices = contour->GetNumberOfVertices();
mitk::Point3D p3;
p3[0] = p3[1] = p3[2] = 4;
contour->AddVertex(p3);
int newNumberOfVertices = contour->GetNumberOfVertices();
MITK_TEST_CONDITION(oldNumberOfVertices != newNumberOfVertices, "vertices added to closed contour");
}
// Test concatenating two contours
static void TestConcatenate()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New();
mitk::Point3D p3;
p3[0] = -2;
p3[1] = 10;
p3[2] = 0;
contour2->AddVertex(p3);
mitk::Point3D p4;
p4[0] = -3;
p4[1] = 6;
p4[2] = -5;
contour2->AddVertex(p4);
contour->Concatenate(contour2);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 4, "two contours were concatenated");
}
// Try to select a vertex at position (within a epsilon of course) where no vertex is.
// So the selected verted member should be null.
static void TestSelectVertexAtWrongPosition()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
MITK_TEST_CONDITION_REQUIRED(contour->GetSelectedVertex() == nullptr, "selected vertex is nullptr");
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 2;
contour->SelectVertexAt(p2, 0.1);
MITK_TEST_CONDITION(contour->GetSelectedVertex() == nullptr, "Vertex was not selected");
}
static void TestInsertVertex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
mitk::Point3D pointToInsert;
pointToInsert[0] = pointToInsert[1] = pointToInsert[2] = 10;
contour->InsertVertexAtIndex(pointToInsert, 1);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 3, "test insert vertex");
MITK_TEST_CONDITION(contour->GetVertexAt(1)->Coordinates == pointToInsert, "compare inserted vertex");
mitk::Point3D outOfTimeBoundPoint;
outOfTimeBoundPoint[0] = outOfTimeBoundPoint[1] = outOfTimeBoundPoint[2] = 1;
contour->InsertVertexAtIndex(outOfTimeBoundPoint, 4, false, mitk::TimeStepType(1));
MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Insert a vertex to an unsupported time step has not changed geometry.");
MITK_TEST_CONDITION(contour->IsEmptyTimeStep(1), "Insert a vertex to an unsupported time step has not added an contour element.");
MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == -1, "Insert a vertex to an unsupported time step has not added an contour element.");
}
// try to access an invalid timestep
static void TestInvalidTimeStep()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
mitk::TimeStepType invalidTimeStep = 42;
MITK_TEST_CONDITION_REQUIRED(contour->IsEmptyTimeStep(invalidTimeStep), "invalid timestep required");
MITK_TEST_FOR_EXCEPTION(std::exception, contour->IteratorBegin(-1));
contour->Close(invalidTimeStep);
MITK_TEST_CONDITION(contour->IsClosed() == false, "test close for timestep 0");
MITK_TEST_CONDITION(contour->IsClosed(invalidTimeStep) == false, "test close at invalid timestep");
contour->SetClosed(true, invalidTimeStep);
MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1,
"test number of vertices at invalid timestep");
contour->AddVertex(p2, invalidTimeStep);
MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test add vertex at invalid timestep");
contour->InsertVertexAtIndex(p2, 0, false, invalidTimeStep);
MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test insert vertex at invalid timestep");
MITK_TEST_CONDITION(contour->SelectVertexAt(0, invalidTimeStep) == false, "test select vertex at invalid timestep");
MITK_TEST_CONDITION(contour->RemoveVertexAt(0, invalidTimeStep) == false, "test remove vertex at invalid timestep");
MITK_TEST_CONDITION(contour->GetVertexAt(0, 5) == nullptr, "Access a vertex on an invalid time step.");
MITK_TEST_CONDITION(contour->GetVertexAt(10, 0) == nullptr, "Access a vertex on an invalid index.");
}
static void TestEmptyContour()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
- MITK_TEST_CONDITION(contour->IteratorBegin() == contour->IteratorEnd(), "test iterator of emtpy contour");
+ MITK_TEST_CONDITION(contour->IteratorBegin() == contour->IteratorEnd(), "test iterator of empty contour");
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "test numberof vertices of empty contour");
}
static void TestSetVertices()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D newCoordinates;
newCoordinates[0] = newCoordinates[1] = newCoordinates[2] = 1;
contour->SetVertexAt(0, newCoordinates);
MITK_TEST_CONDITION(mitk::Equal(contour->GetVertexAt(0)->Coordinates, newCoordinates), "set coordinates");
mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New();
mitk::Point3D p3;
p3[0] = -2;
p3[1] = 10;
p3[2] = 0;
contour2->AddVertex(p3);
mitk::Point3D p4;
p4[0] = -3;
p4[1] = 6;
p4[2] = -5;
contour2->AddVertex(p4);
contour->AddVertex(p);
contour->SetVertexAt(1, contour2->GetVertexAt(1), 0);
MITK_TEST_CONDITION(
mitk::Equal(contour->GetVertexAt(1)->Coordinates, contour2->GetVertexAt(1)->Coordinates), "Use setter and getter combination");
}
static void TestContourModelAPI()
{
mitk::ContourModel::Pointer contour1 = mitk::ContourModel::New();
mitk::Point3D p1;
p1[0] = -2;
p1[1] = 10;
p1[2] = 0;
contour1->AddVertex(p1);
// adding vertices should always copy the content and not store pointers or references.
MITK_TEST_CONDITION(&p1 != &(contour1->GetVertexAt(0)->Coordinates), "copied point");
mitk::Point3D p2;
p2[0] = -3;
p2[1] = 6;
p2[2] = -5;
contour1->AddVertex(p2);
// test use of setter and getter with const and non-const pointers
const mitk::ContourModel::VertexType *vertex = contour1->GetVertexAt(1);
MITK_TEST_CONDITION(contour1->GetIndex(vertex) == 1, "Get index");
auto *nonConstVertex = const_cast<mitk::ContourModel::VertexType *>(vertex);
MITK_TEST_CONDITION(contour1->GetIndex(nonConstVertex) == 1, "Get index non-const");
mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New();
contour2->AddVertex(*(contour1->GetVertexAt(0)));
MITK_TEST_CONDITION(contour2->GetNumberOfVertices() == 1, "Add call with another contour");
}
static void TestClear()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
contour->Expand(3);
contour->Expand(3);
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
p[0] = p[1] = p[2] = 1;
contour->AddVertex(p, mitk::TimeStepType(1));
p[0] = p[1] = p[2] = 2;
contour->AddVertex(p, mitk::TimeStepType(2));
contour->Clear(1);
MITK_TEST_CONDITION(contour->GetTimeSteps() == 3, "Check time step count stays 3.");
MITK_TEST_CONDITION(!contour->IsEmpty(0), "Check time step 0 is not empty.");
MITK_TEST_CONDITION(contour->IsEmpty(1), "Check time step 1 is empty.");
MITK_TEST_CONDITION(!contour->IsEmpty(2), "Check time step 2 is not empty.");
MITK_TEST_CONDITION(contour->GetVertexAt(0, 2)->Coordinates == p, "compare if vertex at t == 2 is still the same");
contour->Clear();
MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Check time step count stays 1.");
MITK_TEST_CONDITION(contour->IsEmpty(0), "Check time step 0 is empty.");
}
int mitkContourModelTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkContourModelTest")
TestAddVertex();
TestSelectVertexAtIndex();
TestSelectVertexAtWorldposition();
TestMoveSelectedVertex();
TestRemoveVertexAtIndex();
TestRemoveVertexAtWorldPosition();
TestIsclosed();
TestConcatenate();
TestInvalidTimeStep();
TestInsertVertex();
TestEmptyContour();
TestSetVertices();
TestSelectVertexAtWrongPosition();
TestContourModelAPI();
TestClear();
MITK_TEST_END()
}
diff --git a/Modules/Core/TestingHelper/include/mitkTestFixture.h b/Modules/Core/TestingHelper/include/mitkTestFixture.h
index 971b9f6248..a6e089f7e7 100644
--- a/Modules/Core/TestingHelper/include/mitkTestFixture.h
+++ b/Modules/Core/TestingHelper/include/mitkTestFixture.h
@@ -1,122 +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.
============================================================================*/
#ifndef mitkTestFixture_h
#define mitkTestFixture_h
#include <cppunit/TestFixture.h>
#include <mitkTestingConfig.h>
#include <itksys/SystemTools.hxx>
#include <string>
#include <vector>
namespace mitk
{
/**
* \brief Test fixture for parameterized tests
*
* This class is a drop-in replacement for CppUnit::TextFixture and
* enables test methods to access individual parameters. You can also
* invoke one method multiple times with different parameters.
*
*
* The following simple example creates a single test without custom
* parameters:
*
* \code
* class MySimpleTestSuite : public mitk::TestFixture
* {
* CPPUNIT_TEST_SUITE(MySimpleTestSuite);
* MITK_TEST(FivePlusFiveTest);
* CPPUNIT_TEST_SUITE_END();
*
* public:
* void FivePlusFiveTest()
* {
* CPPUNIT_ASSERT(5+5 == 10);
* }
* };
* MITK_TEST_SUITE_REGISTRATION(MySimpleTestSuite)
* \endcode
*
*
* The following example creates a test class containing only
* one test method, but the associated test suite contains three tests,
* using different parameters for each call of the same method. Use
* the macro MITK_PARAMETERIZED_TEST_1 only if you know what you are
* doing. If you are not sure, use MITK_TEST instead.
*
* \code
* class MyTestSuite : public mitk::TestFixture
* {
* CPPUNIT_TEST_SUITE(MyTestSuite);
* MITK_PARAMETERIZED_TEST_1(TestSomething, "One");
* MITK_PARAMETERIZED_TEST_1(TestSomething, "Two");
* MITK_PARAMETERIZED_TEST_1(TestSomething, "Three");
* CPPUNIT_TEST_SUITE_END();
*
* public:
*
* void TestSomething()
* {
* std::vector<std::string> parameter = GetTestParameter();
* CPPUNIT_ASSERT(parameter.size() == 1);
* std::string testParam = parameter[0];
*
* MITK_INFO << "Parameter: " << testParam;
* }
* };
* MITK_TEST_SUITE_REGISTRATION(MyTestSuite)
* \endcode
*
* \sa MITK_PARAMETERIZED_TEST
* \sa MITK_PARAMETERIZED_TEST_1
*/
class TestFixture : public CppUnit::TestFixture
{
protected:
/**
* \brief Get parameters for this test fixture
*
* This method can be called in tests added via the MITK_PARAMETERIZED_TEST
* macro or one of its variants.
*
* \return The list of \c std::string parameters passed to previous calls
* of the MITK_PARAMETERIZED_TEST macro or one of its variants.
*
*/
std::vector<std::string> GetTestParameter() const { return m_Parameter; }
/**
* \brief Get the absolute path for test data.
*
- * \param testData The realative path in the MITK test data repository.
+ * \param testData The relative path in the MITK test data repository.
*
* \return The absolute path for the test data.
*/
static std::string GetTestDataFilePath(const std::string &testData)
{
if (itksys::SystemTools::FileIsFullPath(testData.c_str()))
return testData;
return std::string(MITK_DATA_DIR) + "/" + testData;
}
private:
template <class P>
friend class TestCaller;
std::vector<std::string> m_Parameter;
};
}
#endif
diff --git a/Modules/Core/include/mitkBaseRenderer.h b/Modules/Core/include/mitkBaseRenderer.h
index a6ad38fd83..08290199dc 100644
--- a/Modules/Core/include/mitkBaseRenderer.h
+++ b/Modules/Core/include/mitkBaseRenderer.h
@@ -1,506 +1,506 @@
/*============================================================================
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 mitkBaseRenderer_h
#define mitkBaseRenderer_h
#include <mitkDataStorage.h>
#include <mitkPlaneGeometry.h>
#include <mitkPlaneGeometryData.h>
#include <mitkTimeGeometry.h>
#include <mitkCameraController.h>
#include <mitkCameraRotationController.h>
#include <mitkSliceNavigationController.h>
#include <mitkTimeNavigationController.h>
#include <mitkBindDispatcherInteractor.h>
#include <mitkDispatcher.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <map>
#include <set>
namespace mitk
{
class Mapper;
class BaseLocalStorageHandler;
#pragma GCC visibility push(default)
itkEventMacroDeclaration(RendererResetEvent, itk::AnyEvent);
#pragma GCC visibility pop
/*
* \brief Organizes the rendering process
*
* A BaseRenderer contains a reference to a given vtkRenderWindow
* and a corresponding vtkRenderer.
* The BaseRenderer defines which mapper should be used (2D / 3D)
* and which view direction should be rendered.
*
* All existing BaseRenderer are stored in a static variable
* that can be accessed / modified via the static functions.
* VtkPropRenderer is a concrete implementation of a BaseRenderer.
*/
class MITKCORE_EXPORT BaseRenderer : public itk::Object
{
public:
typedef std::map<vtkRenderWindow*, BaseRenderer*> BaseRendererMapType;
static BaseRendererMapType baseRendererMap;
/**
* \brief Defines which kind of mapper (e.g. 2D or 3D) should be used.
*/
enum StandardMapperSlot
{
Standard2D = 1,
Standard3D = 2
};
static BaseRenderer* GetInstance(vtkRenderWindow* renderWindow);
static void AddInstance(vtkRenderWindow* renderWindow, BaseRenderer* baseRenderer);
static void RemoveInstance(vtkRenderWindow* renderWindow);
static BaseRenderer* GetByName(const std::string& name);
static vtkRenderWindow* GetRenderWindowByName(const std::string& name);
/**
* \brief Get a map of specific RenderWindows
*/
static BaseRendererMapType GetSpecificRenderWindows(MapperSlotId mapper);
/**
* \brief Convenience function: Get a map of all 2D RenderWindows
*/
static BaseRendererMapType GetAll2DRenderWindows();
/**
* \brief Convenience function: Get a map of all 3D RenderWindows
*/
static BaseRendererMapType GetAll3DRenderWindows();
mitkClassMacroItkParent(BaseRenderer, itk::Object);
BaseRenderer(const char* name = nullptr, vtkRenderWindow* renderWindow = nullptr);
void RemoveAllLocalStorages();
void RegisterLocalStorageHandler(BaseLocalStorageHandler* lsh);
void UnregisterLocalStorageHandler(BaseLocalStorageHandler* lsh);
virtual void SetDataStorage(DataStorage* storage);
virtual DataStorage::Pointer GetDataStorage() const
{
return m_DataStorage.GetPointer();
}
vtkRenderWindow* GetRenderWindow() const
{
return m_RenderWindow;
}
vtkRenderer* GetVtkRenderer() const
{
return m_VtkRenderer;
}
/**
* \brief Get the dispatcher, which handles events for this base renderer.
*/
Dispatcher::Pointer GetDispatcher() const;
/**
* \brief Set a new size for the render window.
*/
virtual void Resize(int w, int h);
/**
* \brief Initialize the base renderer with a vtk render window.
* Set the new renderer for the camera controller.
*/
virtual void InitRenderer(vtkRenderWindow* renderwindow);
/**
* \brief Set the initial size for the render window.
*/
virtual void InitSize(int w, int h);
virtual void DrawOverlayMouse(Point2D&)
{
MITK_INFO << "BaseRenderer::DrawOverlayMouse() should be in concret implementation OpenGLRenderer." << std::endl;
}
/**
* \brief Set the world time geometry using the given TimeGeometry.
*
* Setting a new world time geometry updates the current world geometry and the
- * curent world plane geometry, using the currently selected slice and timestep.
+ * current world plane geometry, using the currently selected slice and timestep.
*/
virtual void SetWorldTimeGeometry(const TimeGeometry* geometry);
itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry);
/**
* \brief Set the interaction reference world time geometry using the given TimeGeometry.
*
* Setting a new interaction reference world time geometry also updates the
* alignment status of the reference geometry, which can be retrieved using
* 'GetReferenceGeometryAligned'.
* Using a nullptr as the interaction reference geomertry implies that
* no requirements on the geometry exist, thus in this case any check
* will result in 'ReferenceGeometryAligned' being true.
*
* \param geometry The reference geometry used for render window interaction.
*/
virtual void SetInteractionReferenceGeometry(const TimeGeometry* geometry);
/**
* \brief Get the current interaction reference geometry.
*/
itkGetConstObjectMacro(InteractionReferenceGeometry, TimeGeometry);
/**
* \brief Return if the reference geometry aligns with the base renderer's world geometry.
* If true, the interaction reference geometry aligns with the base renderer's
* current world geometry. False otherwise.
*/
itkGetMacro(ReferenceGeometryAligned, bool);
/**
* \brief Get the current time-extracted 3D-geometry.
*/
itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry);
/**
* \brief Get the current slice-extracted 2D-geometry.
*/
itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry);
virtual bool SetWorldGeometryToDataStorageBounds()
{
return false;
}
/**
* \brief Set the slice that should be used for geometry extraction.
*
* The slice defines the current slice-extracted 2D-geometry (CurrentWorldPlaneGeometry).
* Setting a new slice will update the current world geometry and the
- * curent world plane geometry.
+ * current world plane geometry.
*/
virtual void SetSlice(unsigned int slice);
itkGetConstMacro(Slice, unsigned int);
/**
* \brief Set the timestep that should be used for geometry extraction.
*
* The timestep defines the current time-extracted 3D-geometry (CurrentWorldGeometry).
* Setting a new timestep will update the current world geometry and the
- * curent world plane geometry.
+ * current world plane geometry.
*/
virtual void SetTimeStep(unsigned int timeStep);
itkGetConstMacro(TimeStep, unsigned int);
/**
* \brief Get the timestep of a BaseData object which
* exists at the time of the currently displayed content.
*
* Returns -1 if there is no data at the current time.
*/
TimeStepType GetTimeStep(const BaseData* data) const;
/**
* \brief Get the time in ms of the currently display content (geometry).
*/
ScalarType GetTime() const;
/**
* \brief Set the world time geometry using the geometry of the given event.
*
* The function is triggered by a SliceNavigationController::GeometrySendEvent.
*/
virtual void SetGeometry(const itk::EventObject& geometrySliceEvent);
/**
* \brief Set the current world plane geometry using the existing current world geometry.
*
* The function is triggered by a SliceNavigationController::GeometryUpdateEvent.
*/
virtual void UpdateGeometry(const itk::EventObject& geometrySliceEvent);
/**
* \brief Set the current slice using "SetSlice" and update the current world geometry
* and the current world plane geometry.
*
* The function is triggered by a SliceNavigationController::GeometrySliceEvent.
*/
virtual void SetGeometrySlice(const itk::EventObject& geometrySliceEvent);
/**
* \brief Set the current time using "SetTimeStep" and update the current world geometry
* and the current world plane geometry.
*
* The function is triggered by a TimeNavigationController::TimeEvent.
*/
virtual void SetGeometryTime(const itk::EventObject& geometryTimeEvent);
itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode);
/**
* \brief Modify the update time of the current world plane geometry and force reslicing.
*/
void SendUpdateSlice();
/**
* \brief Get timestamp of the update time of the current world plane geometry.
*/
itkGetMacro(CurrentWorldPlaneGeometryUpdateTime, unsigned long);
/**
* \brief Get timestamp of the update time of the current timestep.
*/
itkGetMacro(TimeStepUpdateTime, unsigned long);
/**
* \brief Pick a world coordinate (x,y,z) given a display coordinate (x,y).
*
* \warning Not implemented; has to be overwritten in subclasses.
*/
virtual void PickWorldPoint(const Point2D& diplayPosition, Point3D& worldPosition) const = 0;
/**
* \brief Determines the object (mitk::DataNode) closest to the current
* position by means of picking.
*
* \warning Implementation currently empty for 2D rendering; intended to be
* implemented for 3D renderers.
*/
virtual DataNode* PickObject(const Point2D& /*displayPosition*/, Point3D& /*worldPosition*/) const
{
return nullptr;
}
/**
* \brief Get the currently used mapperID.
*/
itkGetMacro(MapperID, MapperSlotId);
itkGetConstMacro(MapperID, MapperSlotId);
/**
* \brief Set the used mapperID.
*/
virtual void SetMapperID(MapperSlotId id);
virtual int* GetSize() const;
virtual int* GetViewportSize() const;
void SetSliceNavigationController(SliceNavigationController* SlicenavigationController);
itkGetObjectMacro(CameraController, CameraController);
itkGetObjectMacro(SliceNavigationController, SliceNavigationController);
itkGetObjectMacro(CameraRotationController, CameraRotationController);
itkGetMacro(EmptyWorldGeometry, bool);
/**
* \brief Getter/Setter for defining if the displayed region should be shifted
* or rescaled if the render window is resized.
*/
itkGetMacro(KeepDisplayedRegion, bool);
itkSetMacro(KeepDisplayedRegion, bool);
/**
* \brief Return the name of the base renderer
*/
const char* GetName() const
{
return m_Name.c_str();
}
/**
* \brief Return the size in x-direction of the base renderer.
*/
int GetSizeX() const
{
return this->GetSize()[0];
}
/**
* \brief Return the size in y-direction of the base renderer.
*/
int GetSizeY() const
{
return this->GetSize()[1];
}
/**
* \brief Return the bounds of the bounding box of the
* current world geometry (time-extracted 3D-geometry).
*
* If the geometry is empty, the bounds are set to zero.
*/
const double* GetBounds() const;
void RequestUpdate();
void ForceImmediateUpdate();
/**
* \brief Return the number of mappers which are visible and have
* level-of-detail rendering enabled.
*/
unsigned int GetNumberOfVisibleLODEnabledMappers() const;
/**
* \brief Convert a display point to the 3D world index
* using the geometry of the renderWindow.
*/
void DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const;
/**
* \brief Convert a display point to the 2D world index, mapped onto the display plane
* using the geometry of the renderWindow.
*/
void DisplayToPlane(const Point2D& displayPoint, Point2D& planePointInMM) const;
/**
* \brief Convert a 3D world index to the display point
* using the geometry of the renderWindow.
*/
void WorldToDisplay(const Point3D& worldIndex, Point2D& displayPoint) const;
/**
* \brief Convert a 3D world index to the point on the viewport
* using the geometry of the renderWindow.
*/
void WorldToView(const Point3D& worldIndex, Point2D& viewPoint) const;
/**
* \brief Convert a 2D plane coordinate to the display point
* using the geometry of the renderWindow.
*/
void PlaneToDisplay(const Point2D& planePointInMM, Point2D& displayPoint) const;
/**
* \brief Convert a 2D plane coordinate to the point on the viewport
* using the geometry of the renderWindow.
*/
void PlaneToView(const Point2D& planePointInMM, Point2D& viewPoint) const;
double GetScaleFactorMMPerDisplayUnit() const;
Point2D GetDisplaySizeInMM() const;
Point2D GetViewportSizeInMM() const;
Point2D GetOriginInMM() const;
itkGetConstMacro(ConstrainZoomingAndPanning, bool)
virtual void SetConstrainZoomingAndPanning(bool constrain);
protected:
~BaseRenderer() override;
virtual void Update() = 0;
vtkRenderWindow* m_RenderWindow;
vtkRenderer* m_VtkRenderer;
MapperSlotId m_MapperID;
DataStorage::Pointer m_DataStorage;
unsigned long m_LastUpdateTime;
CameraController::Pointer m_CameraController;
CameraRotationController::Pointer m_CameraRotationController;
SliceNavigationController::Pointer m_SliceNavigationController;
void UpdateCurrentGeometries();
virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry* geometry2d);
virtual void SetCurrentWorldGeometry(const BaseGeometry *geometry);
private:
/**
* \brief Pointer to the current TimeGeometry.
*
* This WorldTimeGeometry is used to extract a SlicedGeometry3D,
* using the current timestep (set via SetTimeStep).
* The time-extracted 3D-geometry is used as the "CurrentWorldGeometry".
* A PlaneGeometry can further be extracted using the current slice (set via SetSlice).
* The slice-extracted 2D-geometry is used as the "CurrentWorldPlaneGeometry".
*/
TimeGeometry::ConstPointer m_WorldTimeGeometry;
/**
* \brief Pointer to the interaction reference geometry used for interaction.
*
* This InteractionReferenceGeometry is used to decide if a base renderer /
* render window is able to correctly handle display interaction, e.g. drawing.
* It will be set using the "SetInteractionReferenceGeometry"-function.
*/
TimeGeometry::ConstPointer m_InteractionReferenceGeometry;
/**
* \brief Pointer to the current time-extracted 3D-geometry.
*
* This CurrentWorldGeometry is used to define the bounds for this
* BaseRenderer.
* It will be set using the "SetCurrentWorldGeometry"-function.
*/
BaseGeometry::ConstPointer m_CurrentWorldGeometry;
/**
* \brief Pointer to the current slice-extracted 2D-geometry.
*
* This CurrentWorldPlaneGeometry is used to define the maximal
* area (2D manifold) to be rendered in case we are doing 2D-rendering.
* It will be set using the "SetCurrentWorldPlaneGeometry"-function.
*/
PlaneGeometry::Pointer m_CurrentWorldPlaneGeometry;
unsigned int m_Slice;
unsigned int m_TimeStep;
itk::TimeStamp m_CurrentWorldPlaneGeometryUpdateTime;
itk::TimeStamp m_TimeStepUpdateTime;
BindDispatcherInteractor* m_BindDispatcherInteractor;
bool m_KeepDisplayedRegion;
bool m_ReferenceGeometryAligned;
protected:
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData;
DataNode::Pointer m_CurrentWorldPlaneGeometryNode;
unsigned long m_CurrentWorldPlaneGeometryTransformTime;
std::string m_Name;
double m_Bounds[6];
bool m_EmptyWorldGeometry;
typedef std::set<Mapper*> LODEnabledMappersType;
unsigned int m_NumberOfVisibleLODEnabledMappers;
std::list<BaseLocalStorageHandler*> m_RegisteredLocalStorageHandlers;
bool m_ConstrainZoomingAndPanning;
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h b/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h
index 729fac961c..854d6cd673 100644
--- a/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h
+++ b/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h
@@ -1,44 +1,44 @@
/*============================================================================
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 mitkIOMetaInformationPropertyConstants_h
#define mitkIOMetaInformationPropertyConstants_h
#include <MitkCoreExports.h>
#include "mitkPropertyKeyPath.h"
namespace mitk
{
/**
* @ingroup IO
* @brief The IOMetaInformationPropertyConstants struct
*/
struct MITKCORE_EXPORT IOMetaInformationPropertyConstants
{
//Path to the property containing the name of the reader used
static PropertyKeyPath READER_DESCRIPTION();
//Path to the property containing the version of mitk used to read the data
static PropertyKeyPath READER_VERSION();
//Path to the property containing the mine name detected used to read the data
static PropertyKeyPath READER_MIME_NAME();
//Path to the property containing the mime category detected to read the data
static PropertyKeyPath READER_MIME_CATEGORY();
//Path to the property containing the input location if loaded by file used to read the data
static PropertyKeyPath READER_INPUTLOCATION();
- //Path to the properties containing the reader optins used to read the data
+ //Path to the properties containing the reader options used to read the data
static PropertyKeyPath READER_OPTION_ROOT();
static PropertyKeyPath READER_OPTIONS_ANY();
};
}
#endif
diff --git a/Modules/Core/include/mitkIOUtil.h b/Modules/Core/include/mitkIOUtil.h
index 0c07208d68..cff4b3d829 100644
--- a/Modules/Core/include/mitkIOUtil.h
+++ b/Modules/Core/include/mitkIOUtil.h
@@ -1,444 +1,444 @@
/*============================================================================
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 mitkIOUtil_h
#define mitkIOUtil_h
#include <MitkCoreExports.h>
#include <mitkDataStorage.h>
#include <mitkImage.h>
#include <mitkPointSet.h>
#include <mitkSurface.h>
#include <mitkFileReaderSelector.h>
#include <mitkFileWriterSelector.h>
#include <mitkIFileReader.h>
#include <mitkIFileWriter.h>
#include <fstream>
#if !defined(MITK_WINDOWS_NO_UNDEF) && defined(GetTempPath)
#undef GetTempPath
#endif
namespace us
{
class ModuleResource;
}
namespace mitk
{
class PropertyList;
/**
* \ingroup IO
*
* \brief A utility class to load and save data from/to the local file system.
*
* \see QmitkIOUtil
*/
class MITKCORE_EXPORT IOUtil
{
public:
/**Struct that contains information regarding the current loading process. (e.g. Path that should be loaded,
all found readers for the load path,...). It is set be IOUtil and used to pass information via the option callback
in load operations.
*/
struct MITKCORE_EXPORT LoadInfo
{
LoadInfo(const std::string &path);
std::string m_Path;
std::vector<BaseData::Pointer> m_Output;
FileReaderSelector m_ReaderSelector;
bool m_Cancel;
const PropertyList* m_Properties;
};
/**Struct that is the base class for option callbacks used in load operations. The callback is used by IOUtil, if
more than one suitable reader was found or the a reader contains options that can be set. The callback allows to
change option settings and select the reader that should be used (via loadInfo).
*/
struct MITKCORE_EXPORT ReaderOptionsFunctorBase
{
virtual bool operator()(LoadInfo &loadInfo) const = 0;
};
struct MITKCORE_EXPORT SaveInfo
{
SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path);
bool operator<(const SaveInfo &other) const;
/// The BaseData object to save.
const BaseData *m_BaseData;
/// Contains a set of IFileWriter objects.
FileWriterSelector m_WriterSelector;
/// The selected mime-type, used to restrict results from FileWriterSelector.
MimeType m_MimeType;
/// The path to write the BaseData object to.
std::string m_Path;
/// Flag indicating if sub-sequent save operations are to be canceled.
bool m_Cancel;
};
/**Struct that is the base class for option callbacks used in save operations. The callback is used by IOUtil, if
more than one suitable writer was found or the a writer contains options that can be set. The callback allows to
change option settings and select the writer that should be used (via saveInfo).
*/
struct MITKCORE_EXPORT WriterOptionsFunctorBase
{
virtual bool operator()(SaveInfo &saveInfo) const = 0;
};
/**
* Get the file system path where the running executable is located.
*
* @return The location of the currently running executable, without the filename.
*/
static std::string GetProgramPath();
/**
* Get the default temporary path.
*
* @return The default path for temporary data.
*/
static std::string GetTempPath();
/**
* Returns the Directory Seperator for the current OS.
*
* @return the Directory Seperator for the current OS, i.e. "\\" for Windows and "/" otherwise.
*/
static char GetDirectorySeparator();
/**
* Create and open a temporary file.
*
* This method generates a unique temporary filename from \c templateName, creates
* and opens the file using the output stream \c tmpStream and returns the name of
* the newly create file.
*
* The \c templateName argument must contain six consecutive 'X' characters ("XXXXXX")
* and these are replaced with a string that makes the filename unique.
*
* The file is created with read and write permissions for owner only.
*
* @param tmpStream The output stream for writing to the temporary file.
* @param templateName An optional template for the filename.
* @param path An optional path where the temporary file should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @return The filename of the created temporary file.
*
* @throw mitk::Exception if the temporary file could not be created.
*/
static std::string CreateTemporaryFile(std::ofstream &tmpStream,
const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* Create and open a temporary file.
*
* This method generates a unique temporary filename from \c templateName, creates
* and opens the file using the output stream \c tmpStream and the specified open
* mode \c mode and returns the name of the newly create file. The open mode is always
* OR'd with <code>std::ios_base::out | std::ios_base::trunc</code>.
*
* The \c templateName argument must contain six consecutive 'X' characters ("XXXXXX")
* and these are replaced with a string that makes the filename unique.
*
* The file is created with read and write permissions for owner only.
*
* @param tmpStream The output stream for writing to the temporary file.
* @param mode The open mode for the temporary file stream.
* @param templateName An optional template for the filename.
* @param path An optional path where the temporary file should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @return The filename of the created temporary file.
*
* @throw mitk::Exception if the temporary file could not be created.
*/
static std::string CreateTemporaryFile(std::ofstream &tmpStream,
std::ios_base::openmode mode,
const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* Creates an empty temporary file.
*
* This method generates a unique temporary filename from \c templateName and creates
* this file.
*
* The file is created with read and write permissions for owner only.
*
* ---
* This version is potentially unsafe because the created temporary file is not kept open
* and could be used by another process between calling this method and opening the returned
* file path for reading or writing.
* ---
*
* @return The filename of the created temporary file.
* @param templateName An optional template for the filename.
* @param path An optional path where the temporary file should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @throw mitk::Exception if the temporary file could not be created.
*/
static std::string CreateTemporaryFile(const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* Create a temporary directory.
*
* This method generates a uniquely named temporary directory from \c templateName.
* The last set of six consecutive 'X' characters in \c templateName is replaced
* with a string that makes the directory name unique.
*
* The directory is created with read, write and executable permissions for owner only.
*
* @param templateName An optional template for the directory name.
* @param path An optional path where the temporary directory should be created. Defaults
* to the default temp path as returned by GetTempPath().
* @return The filename of the created temporary file.
*
* @throw mitk::Exception if the temporary directory could not be created.
*/
static std::string CreateTemporaryDirectory(const std::string &templateName = "XXXXXX",
std::string path = std::string());
/**
* @brief Load a file into the given DataStorage.
*
* This method calls Load(const std::vector<std::string>&, DataStorage&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param storage A DataStorage object to which the loaded data will be added.
* @param optionsCallback Pointer to a callback instance. The callback is used by
* the load operation if more the suitable reader was found or the reader has options
* that can be set.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static DataStorage::SetOfObjects::Pointer Load(const std::string &path, DataStorage &storage,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
/**
* @brief Load a file into the given DataStorage given user defined IFileReader::Options.
*
* This method calls Load(const std::vector<std::string>&, DataStorage&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param options IFileReader option instance that should be used if selected reader
* has options.
* @param storage A DataStorage object to which the loaded data will be added.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static DataStorage::SetOfObjects::Pointer Load(const std::string &path,
const IFileReader::Options &options,
DataStorage &storage);
/**
* @brief Load a file and return the loaded data.
*
* This method calls Load(const std::vector<std::string>&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param optionsCallback Pointer to a callback instance. The callback is used by
* the load operation if more the suitable reader was found or the reader has options
* that can be set.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static std::vector<BaseData::Pointer> Load(const std::string &path,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
template <typename T>
static typename T::Pointer Load(const std::string& path, const ReaderOptionsFunctorBase *optionsCallback = nullptr)
{
return dynamic_cast<T*>(Load(path, optionsCallback).at(0).GetPointer());
}
/**
* @brief Load a file and return the loaded data.
*
* This method calls Load(const std::vector<std::string>&) with a
* one-element vector.
*
* @param path The absolute file name including the file extension.
* @param options IFileReader option instance that should be used if selected reader
* has options.
* @return The set of added DataNode objects.
* @throws mitk::Exception if \c path could not be loaded.
*
* @sa Load(const std::vector<std::string>&, DataStorage&)
*/
static std::vector<BaseData::Pointer> Load(const std::string &path, const IFileReader::Options &options);
template <typename T>
static typename T::Pointer Load(const std::string& path, const IFileReader::Options &options)
{
return dynamic_cast<T*>(Load(path, options).at(0).GetPointer());
}
/**
* @brief Loads a list of file paths into the given DataStorage.
*
* If an entry in \c paths cannot be loaded, this method will continue to load
* the remaining entries into \c storage and throw an exception afterwards.
*
* @param paths A list of absolute file names including the file extension.
* @param storage A DataStorage object to which the loaded data will be added.
* @param optionsCallback Pointer to a callback instance. The callback is used by
* the load operation if more the suitable reader was found or the reader has options
* that can be set.
* @return The set of added DataNode objects.
* @throws mitk::Exception if an entry in \c paths could not be loaded.
*/
static DataStorage::SetOfObjects::Pointer Load(const std::vector<std::string> &paths, DataStorage &storage,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
static std::vector<BaseData::Pointer> Load(const std::vector<std::string> &paths,
const ReaderOptionsFunctorBase *optionsCallback = nullptr);
/**
* @brief Loads the contents of a us::ModuleResource and returns the corresponding mitk::BaseData
* @param usResource a ModuleResource, representing a BaseData object
* @param mode Optional parameter to set the openmode of the stream
* @return The set of loaded BaseData objects. \c Should contain either one or zero elements, since a resource
* stream
- * respresents one object.
+ * represents one object.
* @throws mitk::Exception if no reader was found for the stream.
*/
static std::vector<BaseData::Pointer> Load(const us::ModuleResource &usResource,
std::ios_base::openmode mode = std::ios_base::in);
template <typename T>
static typename T::Pointer Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in)
{
return dynamic_cast<T*>(Load(usResource, mode).at(0).GetPointer());
}
static BaseData::Pointer Load(const std::string& path, const PropertyList* properties);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param path The path to the image including file name and and optional file extension.
* If no extension is set, the default extension and mime-type for the
* BaseData type of \c data is used.
* @param setPathProperty
* @throws mitk::Exception if no writer for \c data is available or the writer
* is not able to write the image.
*/
static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty = false);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param path The path to the image including file name and an optional file extension.
* If no extension is set, the default extension and mime-type for the
* BaseData type of \c data is used.
* @param options The IFileWriter options to use for the selected writer.
* @param setPathProperty
* @throws mitk::Exception if no writer for \c data is available or the writer
* is not able to write the image.
*/
static void Save(const mitk::BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty = false);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param mimeType The mime-type to use for writing \c data.
* @param path The path to the image including file name and an optional file extension.
* @param addExtension If \c true, an extension according to the given \c mimeType
* is added to \c path if it does not contain one. If \c path already contains
* a file name extension, it is not checked for compatibility with \c mimeType.
* @param setPathProperty
*
* @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is
* available or the writer is not able to write the image.
*/
static void Save(const mitk::BaseData *data,
const std::string &mimeType,
const std::string &path,
bool addExtension = true,
bool setPathProperty = false);
/**
* @brief Save a mitk::BaseData instance.
* @param data The data to save.
* @param mimeType The mime-type to use for writing \c data.
* @param path The path to the image including file name and an optional file extension.
* @param options Configuration data for the used IFileWriter instance.
* @param addExtension If \c true, an extension according to the given \c mimeType
* is added to \c path if it does not contain one. If \c path already contains
* a file name extension, it is not checked for compatibility with \c mimeType.
* @param setPathProperty
*
* @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is
* available or the writer is not able to write the image.
*/
static void Save(const mitk::BaseData *data,
const std::string &mimeType,
const std::string &path,
const mitk::IFileWriter::Options &options,
bool addExtension = true,
bool setPathProperty = false);
/**
* @brief Use SaveInfo objects to save BaseData instances.
*
* This is a low-level method for directly working with SaveInfo objects. Usually,
* the Save() methods taking a BaseData object as an argument are more appropriate.
*
* @param saveInfos A list of SaveInfo objects for saving contained BaseData objects.
* @param setPathProperty
*
* @see Save(const mitk::BaseData*, const std::string&)
*/
static void Save(std::vector<SaveInfo> &saveInfos, bool setPathProperty = false);
protected:
static std::string Load(std::vector<LoadInfo> &loadInfos,
DataStorage::SetOfObjects *nodeResult,
DataStorage *ds,
const ReaderOptionsFunctorBase *optionsCallback);
static std::string Save(const BaseData *data,
const std::string &mimeType,
const std::string &path,
WriterOptionsFunctorBase *optionsCallback,
bool addExtension,
bool setPathProperty);
static std::string Save(std::vector<SaveInfo> &saveInfos,
WriterOptionsFunctorBase *optionsCallback,
bool setPathProperty);
private:
struct Impl;
};
}
#endif
diff --git a/Modules/Core/include/mitkImageAccessorBase.h b/Modules/Core/include/mitkImageAccessorBase.h
index 0b79494dee..851189f2c7 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 <itkImageRegion.h>
#include <itkIndex.h>
#include <itkSmartPointer.h>
#include "mitkImageDataItem.h"
#include <mutex>
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. */
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 <class TPixel, unsigned int VDimension>
friend class ImagePixelReadAccessor;
template <class TPixel, unsigned int VDimension>
friend class ImagePixelWriteAccessor;
public:
typedef itk::SmartPointer<const mitk::Image> 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 */
+ /** \brief System dependent thread method, to prevent recursive mutex access */
ThreadIDType CurrentThreadHandle();
- /** \brief System dependend thread method, to prevent recursive mutex access */
+ /** \brief System dependent 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/mitkItkImageIO.h b/Modules/Core/include/mitkItkImageIO.h
index f64356551f..d9bfe6c73a 100644
--- a/Modules/Core/include/mitkItkImageIO.h
+++ b/Modules/Core/include/mitkItkImageIO.h
@@ -1,102 +1,102 @@
/*============================================================================
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 mitkItkImageIO_h
#define mitkItkImageIO_h
#include "mitkAbstractFileIO.h"
#include <mitkImage.h>
#include <itkImageIOBase.h>
namespace mitk
{
/**
* This class wraps ITK image IO objects as mitk::IFileReader and
* mitk::IFileWriter objects.
*
* Instantiating this class with a given itk::ImageIOBase instance
* will register corresponding MITK reader/writer services for that
* ITK ImageIO object.
* For all ITK ImageIOs that support the serialization of MetaData
* (e.g. nrrd or mhd) the ItkImageIO ensures the serialization
* of Identification UID.
*/
class MITKCORE_EXPORT ItkImageIO : public AbstractFileIO
{
public:
ItkImageIO(itk::ImageIOBase::Pointer imageIO);
ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank);
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
/**Helper function that can be used to convert a MetaDataDictionary into a PropertyList for a certain mimeType.
The function uses the Property serialization service for that.
@param mimeTypeName Mime type that should be assumed for the meta data deserialization.
@param defaultMetaDataKeys Vector of keys that should be assumed as defaults. For defaults no PropertyInfo will be registered
at the PropertyPersistence service, as they are assumed to be handled anyways. For all other keys an info will be registered
- to ensure that they will be serialized again, even if unkown.
+ to ensure that they will be serialized again, even if unknown.
@param dictionary Reference to the meta data dictionary that contains the information that should be extracted.*/
static PropertyList::Pointer ExtractMetaDataAsPropertyList(const itk::MetaDataDictionary& dictionary, const std::string& mimeTypeName, const std::vector<std::string>& defaultMetaDataKeys);
/** Helper function that van be used to extract a raw mitk image for the passed path using the also passed ImageIOBase instance.
Raw means, that only the pixel data and geometry information is loaded. But e.g. no properties etc...*/
static Image::Pointer LoadRawMitkImageFromImageIO(itk::ImageIOBase* imageIO, const std::string& path);
/** Helper function that van be used to extract a raw mitk image for the passed path using the also passed ImageIOBase instance.
Raw means, that only the pixel data and geometry information is loaded. But e.g. no properties etc...*/
static void PreparImageIOToWriteImage(itk::ImageIOBase* imageIO, const Image* image);
static void SavePropertyListAsMetaData(itk::MetaDataDictionary& dictionary, const PropertyList* properties, const std::string& mimeTypeName);
protected:
virtual std::vector<std::string> FixUpImageIOExtensions(const std::string &imageIOName);
virtual void FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType);
// Fills the m_DefaultMetaDataKeys vector with default values
virtual void InitializeDefaultMetaDataKeys();
// -------------- AbstractFileReader -------------
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
private:
ItkImageIO(const ItkImageIO &other);
ItkImageIO *IOClone() const override;
itk::ImageIOBase::Pointer m_ImageIO;
std::vector<std::string> m_DefaultMetaDataKeys;
};
/**Helper function that converts the content of a meta data into a time point vector.
* If MetaData is not valid or cannot be converted an empty vector is returned.*/
MITKCORE_EXPORT std::vector<TimePointType> ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data);
/**Helper function that converts the time points of a passed time geometry to a time point list
and stores it in a itk::MetaDataObject. Use ConvertMetaDataObjectToTimePointList() to convert it back
to a time point list.*/
MITKCORE_EXPORT itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry);
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkMimeType.h b/Modules/Core/include/mitkMimeType.h
index 5135f92c35..8889240191 100644
--- a/Modules/Core/include/mitkMimeType.h
+++ b/Modules/Core/include/mitkMimeType.h
@@ -1,89 +1,89 @@
/*============================================================================
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 mitkMimeType_h
#define mitkMimeType_h
#include <MitkCoreExports.h>
#include <usSharedData.h>
#include <vector>
namespace mitk
{
class CustomMimeType;
/**
* @ingroup IO
*
- * @brief The MimeType class represens a registered mime-type. It is an immutable wrapper for mitk::CustomMimeType
+ * @brief The MimeType class represents a registered mime-type. It is an immutable wrapper for mitk::CustomMimeType
* that makes memory handling easier by providing a stack-object for the user.
*
* If you want to register a new MimeType, use the CustomMimeType class instead. Wrapping will be performed for you
* automatically.
* In all other cases you should use mitk::MimeType when working with mime-types.
*/
class MITKCORE_EXPORT MimeType
{
public:
MimeType();
MimeType(const MimeType &other);
MimeType(const CustomMimeType &x, int rank, long id);
~MimeType();
MimeType &operator=(const MimeType &other);
bool operator==(const MimeType &other) const;
bool operator<(const MimeType &other) const;
/** @see mitk::CustomMimeType::GetName()*/
std::string GetName() const;
/** @see mitk::CustomMimeType::GetCategory()*/
std::string GetCategory() const;
/** @see mitk::CustomMimeType::GetExtensions()*/
std::vector<std::string> GetExtensions() const;
/** @see mitk::CustomMimeType::GetComment()*/
std::string GetComment() const;
/** @see mitk::CustomMimeType::GetFileNameWithoutExtension()*/
std::string GetFilenameWithoutExtension(const std::string &path) const;
/** @see mitk::CustomMimeType::AppliesTo()*/
bool AppliesTo(const std::string &path) const;
/** @see mitk::CustomMimeType::MatchesExtension()*/
bool MatchesExtension(const std::string &path) const;
/** @see mitk::CustomMimeType::IsValid()*/
bool IsValid() const;
/** @see mitk::CustomMimeType::Swap()*/
void Swap(MimeType &m);
private:
struct Impl;
// Use C++11 shared_ptr instead
us::SharedDataPointer<const Impl> m_Data;
};
MITKCORE_EXPORT void swap(MimeType &m1, MimeType &m2);
MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const MimeType &mimeType);
}
#endif
diff --git a/Modules/Core/include/mitkOperationEvent.h b/Modules/Core/include/mitkOperationEvent.h
index 0dc70737fe..d7b4d6cd17 100644
--- a/Modules/Core/include/mitkOperationEvent.h
+++ b/Modules/Core/include/mitkOperationEvent.h
@@ -1,201 +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.
============================================================================*/
#ifndef mitkOperationEvent_h
#define mitkOperationEvent_h
#include "mitkOperation.h"
#include "mitkOperationActor.h"
#include "mitkUndoModel.h"
#include <MitkCoreExports.h>
#include <list>
#include <string>
namespace mitk
{
//##Documentation
//## @brief Represents an entry of the undo or redo stack.
//##
//## This basic entry includes a textual description of the item and a pair of IDs. Static
//## member functions handle creation and incrementing of these IDs.
//##
//## The GroupEventID is intended for logical grouping of several related Operations.
//## Currently this is used only by PointSetDataInteractor. How this is done and when to use
//## GroupEventIDs is still undocumented.
//## @ingroup Undo
class MITKCORE_EXPORT UndoStackItem
{
public:
UndoStackItem(std::string description = "");
virtual ~UndoStackItem();
//##Documentation
//## @brief For combining operations in groups
//##
//## This ID is used in the undo mechanism.
//## For separation of the separate operations
//## If the GroupEventId of two OperationEvents is equal,
//## then they share one group and will be undone in case of Undo(fine==false)
static int GetCurrGroupEventId();
//##Documentation
//## @brief For combining operations in Objects
//##
//## This ID is used in the Undo-Mechanism.
//## For separation of the separate operations
//## If the ObjectEventId of two OperationEvents is equal,
//## then they share one Object and will be undone in all cases of Undo(true and false).
- //## they shal not be separated, because they were produced to realize one object-change.
+ //## they shall not be separated, because they were produced to realize one object-change.
//## for example: OE_statechange and OE_addlastpoint
static int GetCurrObjectEventId();
//##Documentation
//## @brief Returns the GroupEventId for this object
int GetGroupEventId();
//##Documentation
//## @brief Returns the ObjectEventId for this object
int GetObjectEventId();
//##Documentation
//## @brief Returns the textual description of this object
std::string GetDescription();
virtual void ReverseOperations();
virtual void ReverseAndExecute();
//##Documentation
//## @brief Increases the current ObjectEventId
//## For example if a button click generates operations the ObjectEventId has to be incremented to be able to undo
//the
// operations.
//## Difference between ObjectEventId and GroupEventId: The ObjectEventId capsulates all operations caused by one
// event.
//## A GroupEventId capsulates several ObjectEventIds so that several operations caused by several events can be
// undone with one Undo call.
static void IncCurrObjectEventId();
//##Documentation
//## @brief Increases the current GroupEventId
//## For example if a button click generates operations the GroupEventId has to be incremented to be able to undo
//the
// operations.
//## Difference between ObjectEventId and GroupEventId: The ObjectEventId capsulates all operations caused by one
// event.
//## A GroupEventId capsulates several ObjectEventIds so that several operations caused by several events can be
// undone with one Undo call.
static void IncCurrGroupEventId();
protected:
//##Documentation
//## @brief true, if operation and undooperation have been swapped/changed
bool m_Reversed;
private:
static int m_CurrObjectEventId;
static int m_CurrGroupEventId;
int m_ObjectEventId;
int m_GroupEventId;
std::string m_Description;
UndoStackItem(UndoStackItem &); // hide copy constructor
void operator=(const UndoStackItem &); // hide operator=
};
//##Documentation
//## @brief Represents a pair of operations: undo and the according redo.
//##
//## Additionally to the base class UndoStackItem, which only provides a description of an
//## item, OperationEvent does the actual accounting of the undo/redo stack. This class
//## holds two Operation objects (operation and its inverse operation) and the corresponding
//## OperationActor. The operations may be swapped by the
//## undo models, when an OperationEvent is moved from their undo to their redo
//## stack or vice versa.
//##
//## Note, that memory management of operation and undooperation is done by this class.
//## Memory of both objects is freed in the destructor. For this, the method IsValid() is needed which holds
//## information of the state of m_Destination. In case the object referenced by m_Destination is already deleted,
//## isValid() returns false.
//## In more detail if the destination happens to be an itk::Object (often the case), OperationEvent is informed as
//soon
//## as the object is deleted - from this moment on the OperationEvent gets invalid. You should
//## check this flag before you call anything on destination
//##
//## @ingroup Undo
class MITKCORE_EXPORT OperationEvent : public UndoStackItem
{
public:
//## @brief default constructor
OperationEvent(OperationActor *destination,
Operation *operation,
Operation *undoOperation,
std::string description = "");
//## @brief default destructor
//##
//## removes observers if destination is valid
//## and frees memory referenced by m_Operation and m_UndoOperation
~OperationEvent() override;
//## @brief Returns the operation
Operation *GetOperation();
//## @brief Returns the destination of the operations
OperationActor *GetDestination();
friend class UndoModel;
//## @brief Swaps the two operations and sets a flag,
//## that it has been swapped and doOp is undoOp and undoOp is doOp
void ReverseOperations() override;
//##reverses and executes both operations (used, when moved from undo to redo stack)
void ReverseAndExecute() override;
//## @brief returns true if the destination still is present
//## and false if it already has been deleted
virtual bool IsValid();
protected:
void OnObjectDeleted();
private:
// Has to be observed for itk::DeleteEvents.
// When destination is deleted, this stack item is invalid!
OperationActor *m_Destination;
//## reference to the operation
Operation *m_Operation;
//## reference to the undo operation
Operation *m_UndoOperation;
//## hide copy constructor
OperationEvent(OperationEvent &);
//## hide operator=
void operator=(const OperationEvent &);
// observertag used to listen to m_Destination
unsigned long m_DeleteTag;
//## stores if destination is valid or already has been freed
bool m_Invalid;
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPlaneGeometry.h b/Modules/Core/include/mitkPlaneGeometry.h
index 81a0c3c0e1..680c65ea1b 100644
--- a/Modules/Core/include/mitkPlaneGeometry.h
+++ b/Modules/Core/include/mitkPlaneGeometry.h
@@ -1,606 +1,606 @@
/*============================================================================
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 mitkPlaneGeometry_h
#define mitkPlaneGeometry_h
#include <MitkCoreExports.h>
#include <mitkAnatomicalPlanes.h>
#include <mitkBaseGeometry.h>
#include <mitkRestorePlanePositionOperation.h>
#include <vnl/vnl_cross.h>
namespace mitk
{
template <class TCoordRep, unsigned int NPointDimension>
class Line;
typedef Line<ScalarType, 3> Line3D;
/**
* \brief Describes the geometry of a plane object
*
* Describes a two-dimensional manifold, i.e., to put it simply,
* an object that can be described using a 2D coordinate-system.
*
* PlaneGeometry can map points between 3D world coordinates
* (in mm) and the described 2D coordinate-system (in mm) by first projecting
* the 3D point onto the 2D manifold and then calculating the 2D-coordinates
* (in mm). These 2D-mm-coordinates can be further converted into
* 2D-unit-coordinates (e.g., pixels), giving a parameter representation of
* the object with parameter values inside a rectangle
* (e.g., [0,0]..[width, height]), which is the bounding box (bounding range
* in z-direction always [0]..[1]).
*
* A PlaneGeometry describes the 2D representation within a 3D object (derived from BaseGeometry). For example,
* a single CT-image (slice) is 2D in the sense that you can access the
* pixels using 2D-coordinates, but is also 3D, as the pixels are really
* voxels, thus have an extension (thickness) in the 3rd dimension.
*
*
* Optionally, a reference BaseGeometry can be specified, which usually would
* be the geometry associated with the underlying dataset. This is currently
* used for calculating the intersection of inclined / rotated planes
* (represented as PlaneGeometry) with the bounding box of the associated
* BaseGeometry.
*
* \warning The PlaneGeometry are not necessarily up-to-date and not even
* initialized. As described in the previous paragraph, one of the
* Generate-/Copy-/UpdateOutputInformation methods have to initialize it.
* mitk::BaseData::GetPlaneGeometry() makes sure, that the PlaneGeometry is
* up-to-date before returning it (by setting the update extent appropriately
* and calling UpdateOutputInformation).
*
* Rule: everything is in mm (or ms for temporal information) if not
* stated otherwise.
* \ingroup Geometry
*/
class PlaneGeometry;
/** \deprecatedSince{2014_10} This class is deprecated. Please use PlaneGeometry instead. */
DEPRECATED(typedef PlaneGeometry Geometry2D);
/**
* \brief Describes a two-dimensional, rectangular plane
*
* \ingroup Geometry
*/
class MITKCORE_EXPORT PlaneGeometry : public BaseGeometry
{
public:
mitkClassMacro(PlaneGeometry, BaseGeometry);
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
virtual void IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const;
virtual void WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const;
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em vector
//## \a vec_units to world coordinates (in mm)
//## @deprecated First parameter (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
virtual void IndexToWorld(const mitk::Point2D &atPt2d_untis,
const mitk::Vector2D &vec_units,
mitk::Vector2D &vec_mm) const;
//##Documentation
//## @brief Convert (continuous or discrete) index coordinates of a \em vector
//## \a vec_units to world coordinates (in mm)
//## For further information about coordinates types, please see the Geometry documentation
virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const;
//##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
virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm,
const mitk::Vector2D &vec_mm,
mitk::Vector2D &vec_units) const;
//##Documentation
//## @brief Convert world coordinates (in mm) of a \em vector
//## \a vec_mm to (continuous!) index coordinates.
//## For further information about coordinates types, please see the Geometry documentation
virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const;
/**
* \brief Initialize a plane with orientation \a AnatomicalPlane
* (default: axial) with respect to \a BaseGeometry (default: identity).
* Spacing also taken from \a BaseGeometry.
*
* \warning A former version of this method created a geometry with unit
* spacing. For unit spacing use
*
* \code
* // for in-plane unit spacing:
* thisgeometry->SetSizeInUnits(thisgeometry->GetExtentInMM(0),
* thisgeometry->GetExtentInMM(1));
* // additionally, for unit spacing in normal direction (former version
* // did not do this):
* thisgeometry->SetExtentInMM(2, 1.0);
* \endcode
*/
virtual void InitializeStandardPlane(const BaseGeometry *geometry3D,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
ScalarType zPosition = 0,
bool frontside = true,
bool rotated = false,
bool top = true);
/**
* \brief Initialize a plane with orientation \a AnatomicalPlane
* (default: axial) with respect to \a BaseGeometry (default: identity).
* Spacing also taken from \a BaseGeometry.
*
* \param geometry3D
* \param top if \a true, create plane at top, otherwise at bottom
* (for AnatomicalPlane Axial, for other plane locations respectively)
* \param planeorientation
* \param frontside
* \param rotated
*/
virtual void InitializeStandardPlane(const BaseGeometry *geometry3D,
bool top,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
bool frontside = true,
bool rotated = false);
/**
* \brief Initialize a plane with orientation \a AnatomicalPlane
* (default: axial) with respect to \a transform (default: identity)
* given width and height in units.
*
* \a Rotated means rotated by 180 degrees (1/2 rotation) within the plane.
* Rotation by 90 degrees (1/4 rotation) is not implemented as of now.
*
* \a Frontside/Backside:
* Viewed from below = frontside in the axial case;
* (radiologist's view versus neuro-surgeon's view, see:
* http://www.itk.org/Wiki/images/e/ed/DICOM-OrientationDiagram-Radiologist-vs-NeuroSurgeon.png )
* Viewed from front = frontside in the coronal case;
* Viewed from left = frontside in the sagittal case.
*
* \a Cave/Caution: Currently only RPI, LAI, LPS and RAS in the three standard planes are covered,
* i.e. 12 cases of 144: 3 standard planes * 48 coordinate orientations = 144 cases.
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const AffineTransform3D *transform = nullptr,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
ScalarType zPosition = 0,
bool frontside = true,
bool rotated = false,
bool top = true);
/**
* \brief Initialize plane with orientation \a AnatomicalPlane
* (default: axial) given width, height and spacing.
*
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const Vector3D &spacing,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
ScalarType zPosition = 0,
bool frontside = true,
bool rotated = false,
bool top = true);
/**
* \brief Initialize plane by width and height in pixels, right-/down-vector
* (itk) to describe orientation in world-space (vectors will be normalized)
* and spacing (default: 1.0 mm in all directions).
*
* The vectors are normalized and multiplied by the respective spacing before
* they are set in the matrix.
*
* This overloaded version of InitializeStandardPlane() creates only righthanded
* coordinate orientations, unless spacing contains 1 or 3 negative entries.
*
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const Vector3D &rightVector,
const Vector3D &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by width and height in pixels,
* right-/down-vector (vnl) to describe orientation in world-space (vectors
* will be normalized) and spacing (default: 1.0 mm in all directions).
*
* The vectors are normalized and multiplied by the respective spacing
* before they are set in the matrix.
*
* This overloaded version of InitializeStandardPlane() creates only righthanded
* coordinate orientations, unless spacing contains 1 or 3 negative entries.
*
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const VnlVector &rightVector,
const VnlVector &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by right-/down-vector (itk) and spacing
* (default: 1.0 mm in all directions).
*
* The length of the right-/-down-vector is used as width/height in units,
* respectively. Then, the vectors are normalized and multiplied by the
* respective spacing before they are set in the matrix.
*/
virtual void InitializeStandardPlane(const Vector3D &rightVector,
const Vector3D &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by right-/down-vector (vnl) and spacing
* (default: 1.0 mm in all directions).
*
* The length of the right-/-down-vector is used as width/height in units,
* respectively. Then, the vectors are normalized and multiplied by the
* respective spacing before they are set in the matrix.
*/
virtual void InitializeStandardPlane(const VnlVector &rightVector,
const VnlVector &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by origin and normal (size is 1.0 mm in
* all directions, direction of right-/down-vector valid but
* undefined).
* \warning This function can only produce righthanded coordinate orientation, not lefthanded.
*/
virtual void InitializePlane(const Point3D &origin, const Vector3D &normal);
/**
* \brief Initialize plane by right-/down-vector.
*
* \warning The vectors are set into the matrix as they are,
* \em without normalization!
* This function creates a righthanded IndexToWorldTransform,
* only a negative thickness could still make it lefthanded.
*/
void SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness = 1.0);
/**
* \brief Check if matrix is a rotation matrix:
* - determinant is 1?
* - R*R^T is ID?
* Output warning otherwise.
*/
static bool CheckRotationMatrix(AffineTransform3D *transform, double epsilon=1e-6);
/**
* \brief Normal of the plane
*
*/
Vector3D GetNormal() const;
/**
* \brief Normal of the plane as VnlVector
*
*/
VnlVector GetNormalVnl() const;
virtual ScalarType SignedDistance(const Point3D &pt3d_mm) const;
/**
* \brief Calculates, whether a point is below or above the plane. There are two different
*calculation methods, with or without consideration of the bounding box.
*/
virtual bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox = false) const;
/**
* \brief Distance of the point from the plane
* (bounding-box \em not considered)
*
*/
ScalarType DistanceFromPlane(const Point3D &pt3d_mm) const;
/**
* \brief Signed distance of the point from the plane
* (bounding-box \em not considered)
*
* > 0 : point is in the direction of the direction vector.
*/
inline ScalarType SignedDistanceFromPlane(const Point3D &pt3d_mm) const
{
ScalarType len = GetNormalVnl().two_norm();
if (len == 0)
return 0;
return (pt3d_mm - GetOrigin()) * GetNormal() / len;
}
/**
* \brief Distance of the plane from another plane
* (bounding-box \em not considered)
*
* Result is 0 if planes are not parallel.
*/
ScalarType DistanceFromPlane(const PlaneGeometry *plane) const { return fabs(SignedDistanceFromPlane(plane)); }
/**
* \brief Signed distance of the plane from another plane
* (bounding-box \em not considered)
*
* Result is 0 if planes are not parallel.
*/
inline ScalarType SignedDistanceFromPlane(const PlaneGeometry *plane) const
{
if (IsParallel(plane))
{
return SignedDistance(plane->GetOrigin());
}
return 0;
}
/**
* \brief Calculate the intersecting line of two planes
*
* \return \a true planes are intersecting
* \return \a false planes do not intersect
*/
bool IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const;
/**
* \brief Calculate two points where another plane intersects the border of this plane
*
- * \return number of intersection points (0..2). First interection point (if existing)
+ * \return number of intersection points (0..2). First intersection point (if existing)
* is returned in \a lineFrom, second in \a lineTo.
*/
unsigned int IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const;
/**
* \brief Calculate the angle between two planes
*
* \return angle in radiants
*/
double Angle(const PlaneGeometry *plane) const;
/**
* \brief Calculate the angle between the plane and a line
*
* \return angle in radiants
*/
double Angle(const Line3D &line) const;
/**
* \brief Calculate intersection point between the plane and a line
*
* \param line
* \param intersectionPoint intersection point
* \return \a true if \em unique intersection exists, i.e., if line
* is \em not on or parallel to the plane
*/
bool IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const;
/**
* \brief Calculate line parameter of intersection point between the
* plane and a line
*
* \param line
* \param t parameter of line: intersection point is
* line.GetPoint()+t*line.GetDirection()
* \return \a true if \em unique intersection exists, i.e., if line
* is \em not on or parallel to the plane
*/
bool IntersectionPointParam(const Line3D &line, double &t) const;
/**
* \brief Returns whether the plane is parallel to another plane
*
* @return true iff the normal vectors both point to the same or exactly oposit direction
*/
bool IsParallel(const PlaneGeometry *plane) const;
/**
* \brief Returns whether the point is on the plane
* (bounding-box \em not considered)
*/
bool IsOnPlane(const Point3D &point) const;
/**
* \brief Returns whether the line is on the plane
* (bounding-box \em not considered)
*/
bool IsOnPlane(const Line3D &line) const;
/**
* \brief Returns whether the plane is on the plane
* (bounding-box \em not considered)
*
* @return true if the normal vector of the planes point to the same or the exactly oposit direction and
* the distance of the planes is < eps
*
*/
bool IsOnPlane(const PlaneGeometry *plane) const;
/**
* \brief Returns the lot from the point to the plane
*/
Point3D ProjectPointOntoPlane(const Point3D &pt) const;
itk::LightObject::Pointer InternalClone() const override;
/** Implements operation to re-orient the plane */
void ExecuteOperation(Operation *operation) override;
/**
* \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D
* geometry. The result is a 2D point in mm (\a pt2d_mm).
*
* The result is a 2D point in mm (\a pt2d_mm) relative to the upper-left
* corner of the geometry. To convert this point into units (e.g., pixels
* in case of an image), use WorldToIndex.
* \return true projection was possible
* \sa Project(const mitk::Point3D &pt3d_mm, mitk::Point3D
* &projectedPt3d_mm)
*/
virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const;
/**
* \brief Converts a 2D point given in mm (\a pt2d_mm) relative to the
* upper-left corner of the geometry into the corresponding
* world-coordinate (a 3D point in mm, \a pt3d_mm).
*
* To convert a 2D point given in units (e.g., pixels in case of an
* image) into a 2D point given in mm (as required by this method), use
* IndexToWorld.
*/
virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const;
/**
* \brief Set the width and height of this 2D-geometry in units by calling
* SetBounds. This does \a not change the extent in mm!
*
* For an image, this is the number of pixels in x-/y-direction.
* \note In contrast to calling SetBounds directly, this does \a not change
* the extent in mm!
*/
virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height);
/**
* \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D
* geometry. The result is a 3D point in mm (\a projectedPt3d_mm).
*
* \return true projection was possible
*/
virtual bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const;
/**
* \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D
* geometry. The result is a 2D vector in mm (\a vec2d_mm).
*
* The result is a 2D vector in mm (\a vec2d_mm) relative to the
* upper-left
* corner of the geometry. To convert this point into units (e.g., pixels
* in case of an image), use WorldToIndex.
* \return true projection was possible
* \sa Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D
* &projectedVec3d_mm)
*/
virtual bool Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const;
/**
* \brief Converts a 2D vector given in mm (\a vec2d_mm) relative to the
* upper-left corner of the geometry into the corresponding
* world-coordinate (a 3D vector in mm, \a vec3d_mm).
*
* To convert a 2D vector given in units (e.g., pixels in case of an
* image) into a 2D vector given in mm (as required by this method), use
* IndexToWorld.
*/
virtual void Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const;
/**
* \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D
* geometry. The result is a 3D vector in mm (\a projectedVec3d_mm).
*
* DEPRECATED. Use Project(vector,vector) instead
*
* \return true projection was possible
*/
virtual bool Project(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector3D &projectedVec3d_mm) const;
/**
* \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D
* geometry. The result is a 3D vector in mm (\a projectedVec3d_mm).
*
* \return true projection was possible
*/
virtual bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const;
/**
* \brief Distance of the point from the geometry
* (bounding-box \em not considered)
*
*/
inline ScalarType Distance(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); }
/**
* \brief Set the geometrical frame of reference in which this PlaneGeometry
* is placed.
*
* This would usually be the BaseGeometry of the underlying dataset, but
* setting it is optional.
*/
void SetReferenceGeometry(const mitk::BaseGeometry *geometry);
/**
* \brief Get the geometrical frame of reference for this PlaneGeometry.
*/
const BaseGeometry *GetReferenceGeometry() const;
bool HasReferenceGeometry() const;
protected:
PlaneGeometry();
PlaneGeometry(const PlaneGeometry &other);
~PlaneGeometry() override;
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
const mitk::BaseGeometry *m_ReferenceGeometry;
//##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); };
//##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!)
void CheckBounds(const BoundsArrayType &bounds) override;
//##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);.
void CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) override;
private:
/**
* \brief Compares plane with another plane: \a true if IsOnPlane
* (bounding-box \em not considered)
*/
virtual bool operator==(const PlaneGeometry *) const { return false; };
/**
* \brief Compares plane with another plane: \a false if IsOnPlane
* (bounding-box \em not considered)
*/
virtual bool operator!=(const PlaneGeometry *) const { return false; };
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPlanePositionManager.h b/Modules/Core/include/mitkPlanePositionManager.h
index 235688a5a8..4c4f46a52e 100644
--- a/Modules/Core/include/mitkPlanePositionManager.h
+++ b/Modules/Core/include/mitkPlanePositionManager.h
@@ -1,90 +1,90 @@
/*============================================================================
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 mitkPlanePositionManager_h
#define mitkPlanePositionManager_h
#include "mitkCommon.h"
#include "mitkDataStorage.h"
#include "mitkRestorePlanePositionOperation.h"
#include <mitkPlaneGeometry.h>
#include <mitkServiceInterface.h>
class MitkCoreActivator;
namespace mitk
{
/**
The mitk::PlanePositionManagerService holds and manages a list of certain planepositions.
To store a new position you need to specify the first slice of your slicestack and the
slicenumber you want to restore in the mitk::PlanePositionManager::AddNewPlanePosition() function.
To restore a position call mitk::PlanePositionManagerService::GetPlanePosition(ID) where ID is the position
- in the plane positionlist (returned by AddNewPlanePostion). This will give a mitk::RestorePlanePositionOperation
+ in the plane positionlist (returned by AddNewPlanePosition). This will give a mitk::RestorePlanePositionOperation
which can be executed by the SliceNavigationController of the slicestack.
\sa QmitkSegmentationView.cpp
*/
class MITKCORE_EXPORT PlanePositionManagerService
{
public:
PlanePositionManagerService();
~PlanePositionManagerService();
/**
\brief Adds a new plane position to the list. If this geometry is identical to one of the list nothing will be
added
\a plane THE FIRST! slice of the slice stack
\a sliceIndex the slice number of the selected slice
\return returns the ID i.e. the position in the positionlist. If the PlaneGeometry which is to be added already
exists the existing
ID will be returned.
*/
unsigned int AddNewPlanePosition(const mitk::PlaneGeometry *plane, unsigned int sliceIndex = 0);
/**
\brief Removes the plane at the position \a ID from the list.
\a ID the plane ID which should be removed, i.e. its position in the list
\return true if the plane was removed successfully and false if it is an invalid ID
*/
bool RemovePlanePosition(unsigned int ID);
/// \brief Clears the complete positionlist
void RemoveAllPlanePositions();
/**
\brief Getter for a specific plane position with a given ID
\a ID the ID of the plane position
\return Returns a RestorePlanePositionOperation which can be executed by th SliceNavigationController or nullptr for
an
invalid ID
*/
mitk::RestorePlanePositionOperation *GetPlanePosition(unsigned int ID);
/// \brief Getting the number of all stored planes
unsigned int GetNumberOfPlanePositions();
private:
// Disable copy constructor and assignment operator.
PlanePositionManagerService(const PlanePositionManagerService &);
PlanePositionManagerService &operator=(const PlanePositionManagerService &);
std::vector<mitk::RestorePlanePositionOperation *> m_PositionList;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::PlanePositionManagerService, "org.mitk.PlanePositionManagerService")
#endif
diff --git a/Modules/Core/include/mitkPointSetVtkMapper2D.h b/Modules/Core/include/mitkPointSetVtkMapper2D.h
index c4300428b6..3bc9e54ea7 100644
--- a/Modules/Core/include/mitkPointSetVtkMapper2D.h
+++ b/Modules/Core/include/mitkPointSetVtkMapper2D.h
@@ -1,239 +1,239 @@
/*============================================================================
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 mitkPointSetVtkMapper2D_h
#define mitkPointSetVtkMapper2D_h
#include "mitkBaseRenderer.h"
#include "mitkLocalStorageHandler.h"
#include "mitkVtkMapper.h"
#include <MitkCoreExports.h>
#include <mitkPointSetShapeProperty.h>
// VTK
#include <vtkSmartPointer.h>
class vtkActor;
class vtkPropAssembly;
class vtkPolyData;
class vtkPolyDataMapper;
class vtkGlyphSource2D;
class vtkGlyph3D;
class vtkFloatArray;
class vtkCellArray;
namespace mitk
{
class PointSet;
/**
* @brief Vtk-based 2D mapper for PointSet
*
* Due to the need of different colors for selected
* and unselected points and the facts, that we also have a contour and
* labels for the points, the vtk structure is build up the following way:
*
* We have three PolyData, one selected, and one unselected and one
* for a contour between the points. Each one is connected to an own
* PolyDataMapper and an Actor. The different color for the unselected and
* selected state and for the contour is read from properties.
*
* This mapper has several additional functionalities, such as rendering
* a contour between points, calculating and displaying distances or angles
* between points.
*
* @section mitkPointSetVtkMapper2D_point_rep Point Representation
*
* The points are displayed as small glyphs of configurable shape
* (see property "PointSet.2D.shape"). The size of these glyphs
* is given in world units. That means, the size or shape of those
* glyphs is independent of the BaseGeometry object that you assign
* to the PointSet. As for all other objects, _positions_ of points
* will be transformed into the world via the Geometry's index-to-world
* transform.
*
* Then the three Actors are combined inside a vtkPropAssembly and this
* object is returned in GetProp() and so hooked up into the rendering
* pipeline.
*
* @section mitkPointSetVtkMapper2D_propertires Applicable Properties
*
* Properties that can be set for point sets and influence the PointSetVTKMapper2D are:
*
* - \b "line width": (IntProperty 2) // line width of the line from one point to another
* - \b "point line width": (IntProperty 1) // line width of the cross marking a point
* - \b "point 2D size": (FloatProperty 6) // size of the glyph marking a point (diameter, in world
* units!)
* - \b "show contour": (BoolProperty false) // enable contour rendering between points (lines)
* - \b "close contour": (BoolProperty false) // if enabled, the open strip is closed (first point
* connected with last point)
* - \b "show points": (BoolProperty true) // show or hide points
* - \b "show distances": (BoolProperty false) // show or hide distance measure
* - \b "distance decimal digits": (IntProperty 2) // set the number of decimal digits to be shown when
* rendering the distance information
* - \b "show angles": (BoolProperty false) // show or hide angle measurement
* - \b "show distant lines": (BoolProperty false) // show the line between to points from a distant view
* (equals "always on top" option)
* - \b "layer": (IntProperty 1) // default is drawing pointset above images (they have a
* default layer of 0)
* - \b "PointSet.2D.shape" (EnumerationProperty Cross) // provides different shapes marking a point
* 0 = "None", 1 = "Vertex", 2 = "Dash", 3 = "Cross", 4 = "ThickCross", 5 = "Triangle", 6 = "Square", 7 =
* "Circle",
* 8 = "Diamond", 9 = "Arrow", 10 = "ThickArrow", 11 = "HookedArrow", 12 = "Cross"
* - \b "PointSet.2D.fill shape": (BoolProperty false) // fill or do not fill the glyph shape
* - \b "Pointset.2D.distance to plane": (FloatProperty 4.0) //In the 2D render window, points are rendered which lie
* within a certain distance
* to the current plane. They are projected on the current
* plane and scaled according to their distance.
* Point markers appear smaller as the plane moves away
* from
* their true location.
* The distance threshold can be adjusted by this float
* property, which ables the user to delineate the points
* that lie exactly on the plane. (+/- rounding error)
*
* Other Properties used here but not defined in this class:
*
* - \b "selectedcolor": (ColorProperty (1.0f, 0.0f, 0.0f)) // default color of the selected pointset e.g. the
* current
* point is red
* - \b "contourcolor" : (ColorProperty (1.0f, 0.0f, 0.0f)) // default color for the contour is red
* - \b "color": (ColorProperty (1.0f, 1.0f, 0.0f)) // default color of the (unselected) pointset is yellow
* - \b "opacity": (FloatProperty 1.0) // opacity of point set, contours
* - \b "label": (StringProperty nullptr) // a label can be defined for each point, which is rendered in proximity
* to
* the point
*
* @ingroup Mapper
*/
class MITKCORE_EXPORT PointSetVtkMapper2D : public VtkMapper
{
public:
mitkClassMacro(PointSetVtkMapper2D, VtkMapper);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
virtual const mitk::PointSet *GetInput() const;
/** \brief returns the a prop assembly */
vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
/** \brief set the default properties for this mapper */
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
class LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
/* constructor */
LocalStorage();
/* destructor */
~LocalStorage() override;
// points
vtkSmartPointer<vtkPoints> m_UnselectedPoints;
vtkSmartPointer<vtkPoints> m_SelectedPoints;
vtkSmartPointer<vtkPoints> m_ContourPoints;
// scales
vtkSmartPointer<vtkFloatArray> m_UnselectedScales;
vtkSmartPointer<vtkFloatArray> m_SelectedScales;
// distances
vtkSmartPointer<vtkFloatArray> m_DistancesBetweenPoints;
// lines
vtkSmartPointer<vtkCellArray> m_ContourLines;
// glyph source (provides different shapes for the points)
vtkSmartPointer<vtkGlyphSource2D> m_UnselectedGlyphSource2D;
vtkSmartPointer<vtkGlyphSource2D> m_SelectedGlyphSource2D;
// glyph
vtkSmartPointer<vtkGlyph3D> m_UnselectedGlyph3D;
vtkSmartPointer<vtkGlyph3D> m_SelectedGlyph3D;
// polydata
vtkSmartPointer<vtkPolyData> m_VtkUnselectedPointListPolyData;
vtkSmartPointer<vtkPolyData> m_VtkSelectedPointListPolyData;
vtkSmartPointer<vtkPolyData> m_VtkContourPolyData;
// actor
vtkSmartPointer<vtkActor> m_UnselectedActor;
vtkSmartPointer<vtkActor> m_SelectedActor;
vtkSmartPointer<vtkActor> m_ContourActor;
vtkSmartPointer<vtkTextActor> m_VtkTextActor;
std::vector<vtkSmartPointer<vtkTextActor>> m_VtkTextLabelActors;
std::vector<vtkSmartPointer<vtkTextActor>> m_VtkTextDistanceActors;
std::vector<vtkSmartPointer<vtkTextActor>> m_VtkTextAngleActors;
// mappers
vtkSmartPointer<vtkPolyDataMapper> m_VtkUnselectedPolyDataMapper;
vtkSmartPointer<vtkPolyDataMapper> m_VtkSelectedPolyDataMapper;
vtkSmartPointer<vtkPolyDataMapper> m_VtkContourPolyDataMapper;
// propassembly
vtkSmartPointer<vtkPropAssembly> m_PropAssembly;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
protected:
/* constructor */
PointSetVtkMapper2D();
/* destructor */
~PointSetVtkMapper2D() override;
/* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
/* \brief Called in mitk::Mapper::Update
* If TimeGeometry or time step is not valid of point set: reset mapper so that nothing is
* displayed e.g. toggle visibility of the propassembly */
void ResetMapper(BaseRenderer *renderer) override;
/* \brief Fills the vtk objects, thus it is only called when the point set has been changed.
* This function iterates over the input point set and determines the glyphs which lie in a specific
* range around the current slice. Those glyphs are rendered using a specific shape defined in vtk glyph source
* to mark each point. The shape can be changed in MITK using the property "PointSet.2D.shape".
*
* There were issues when rendering vtk glyphs in the 2D-render windows. By default, the glyphs are
* rendered within the x-y plane in each 2D-render window, so you would only see them from the
* side in the sagittal and coronal 2D-render window. The solution to this is to rotate the glyphs in order
- * to be ortogonal to the current view vector. To achieve this, the rotation (vtktransform) of the current
+ * to be orthogonal to the current view vector. To achieve this, the rotation (vtktransform) of the current
* PlaneGeometry is applied to the orientation of the glyphs. */
virtual void CreateVTKRenderObjects(mitk::BaseRenderer *renderer);
// member variables holding the current value of the properties used in this mapper
bool m_ShowContour; // "show contour" property
bool m_CloseContour; // "close contour" property
bool m_ShowPoints; // "show points" property
bool m_ShowDistances; // "show distances" property
int m_DistancesDecimalDigits; // "distance decimal digits" property
bool m_ShowAngles; // "show angles" property
bool m_ShowDistantLines; // "show distant lines" property
int m_LineWidth; // "line width" property
int m_PointLineWidth; // "point line width" property
float m_Point2DSize; // "point 2D size" property
int m_IDShapeProperty; // ID for mitkPointSetShape Enumeration Property "Pointset.2D.shape"
bool m_FillShape; // "Pointset.2D.fill shape" property
float m_DistanceToPlane; // "Pointset.2D.distance to plane" property
bool m_FixedSizeOnScreen; // "Pointset.2D.fixed size on screen" property
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkProgressBarImplementation.h b/Modules/Core/include/mitkProgressBarImplementation.h
index 458335612e..9c39cf1da7 100644
--- a/Modules/Core/include/mitkProgressBarImplementation.h
+++ b/Modules/Core/include/mitkProgressBarImplementation.h
@@ -1,52 +1,52 @@
/*============================================================================
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 mitkProgressBarImplementation_h
#define mitkProgressBarImplementation_h
#include <MitkCoreExports.h>
namespace mitk
{
//##Documentation
- //## @brief GUI independent Interface for all Gui depentent implementations of a ProgressBar.
+ //## @brief GUI independent Interface for all Gui dependent implementations of a ProgressBar.
class MITKCORE_EXPORT ProgressBarImplementation
{
public:
//##Documentation
//## @brief Constructor
ProgressBarImplementation(){};
//##Documentation
//## @brief Destructor
virtual ~ProgressBarImplementation(){};
//##Documentation
//## @brief Sets whether the current progress value is displayed.
virtual void SetPercentageVisible(bool visible) = 0;
//##Documentation
//## @brief Explicitly reset progress bar.
virtual void Reset() = 0;
//##Documentation
//## @brief Adds steps to totalSteps.
virtual void AddStepsToDo(unsigned int steps) = 0;
//##Documentation
//## @brief Sets the current amount of progress to current progress + steps.
//## @param steps the number of steps done since last Progress(int steps) call.
virtual void Progress(unsigned int steps) = 0;
};
} // end namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPropertyKeyPath.h b/Modules/Core/include/mitkPropertyKeyPath.h
index bf0c37101e..ad0334725a 100644
--- a/Modules/Core/include/mitkPropertyKeyPath.h
+++ b/Modules/Core/include/mitkPropertyKeyPath.h
@@ -1,216 +1,216 @@
/*============================================================================
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 mitkPropertyKeyPath_h
#define mitkPropertyKeyPath_h
#include <string>
#include <vector>
#include <mitkExceptionMacro.h>
#include <MitkCoreExports.h>
namespace mitk
{
/** @brief Class that can be used to specify nested or wild carded property keys. E.g.
* for the use in context of the property persistence service or the property relation service.\n
* Following assumptions are made /preconditions are defined:
* - A property key is partitioned by "." into nodes (c.f. visualization of property keys in the PropertyView).
* - A node can either be an element or a selection.
* - An element has a name (alphanumric, - and space; "A-Za-z0-9- ") or is wildcarded ("*")
* - A selection is either an index (e.g. "[1]") or a wildcard ("[*]").
*
- * Selections are used to indicate that the preceding element has multiple occurrences and which occurence is meant.
+ * Selections are used to indicate that the preceding element has multiple occurrences and which occurrence is meant.
* Example property keys would be:
* - prop : A simple property key
* - prop.subprop1 : A property key consisting of two nodes
* - prop.* : Any property key that starts with a node "prop"
* - prop.sub.[2] : A property key that starts with a node "prop" and a has a second node that is selection and has
* the index 2.
* - prop.sub.[*] : Any property key that starts with a node "prop" and a has a second node that is selection (with
* any index).
*
* To build a path one may use the Add* method to build up the PropertyKeyPath element by element.\n
* "first.*.third.[3]" would be equivalent to
* propKeyPath.AddElement("first");
* propKeyPath.AddAnyElement();
* propKeyPath.AddSelection("third",3);\n
* or the inline version
* propKeyPath.AddElement("first").AddAnyElement().AddSelection("third",3);
*/
class MITKCORE_EXPORT PropertyKeyPath final
{
public:
using ItemSelectionIndex = std::size_t;
using ElementNameType = std::string;
struct MITKCORE_EXPORT NodeInfo
{
enum class NodeType
{
Invalid = 0, //*< Node does not exist or is invalid.
Element, //*< Selects an specific element given the node name.
ElementSelection, //*< Selects an specific item in a sequence of items and has a item selector ("[n]").
AnySelection, //*< Selects all items of a specific element ("[*]").
AnyElement //*< Selects any element/item. Node name is wildcarded ("*"); item selection as well implictily.
};
NodeType type;
ElementNameType name;
ItemSelectionIndex selection;
NodeInfo();
NodeInfo(const ElementNameType &name, NodeType type = NodeType::Element, ItemSelectionIndex index = 0);
bool Matches(const NodeInfo &right) const;
bool operator==(const NodeInfo &right) const;
};
using NodeInfoVectorType = std::vector<NodeInfo>;
using PathIndexType = NodeInfoVectorType::size_type;
/** Returns if the PropertyKeyPath is empty.*/
bool IsEmpty() const;
/** Returns if the path is explicit (has no wildcards).*/
bool IsExplicit() const;
/** Returns if the path has any nodes with item selection wild cards ([*]).*/
bool HasItemSelectionWildcardsOnly() const;
/** Number of path nodes the PropertyKeyPath contains.*/
PathIndexType GetSize() const;
/** Adds a new node to the end of the path.
\param [in] newNode Reference to the node that should be added.
\return Returns the index of the newly added node.*/
PathIndexType AddNode(const NodeInfo &newNode);
/** Function returns the node info of a path node specified by the index
* within the PropertyKeyPath.
* \pre Passed index must not be out of bounds.
* \param [in] index Index of the node whose info should be retrieved.
* \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be
* thrown.*/
const NodeInfo &GetNode(const PathIndexType &index) const;
/** Function returns the node info of a path node specified by the index
* within the PropertyKeyPath.
* \pre Passed index must not be out of bounds.
* \param [in] index Index of the node whose info should be retrieved.
* \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be
* thrown.*/
NodeInfo &GetNode(const PathIndexType &index);
/** Function returns the node info of the first path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
NodeInfo &GetFirstNode();
/** Function returns the node info of the first path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
const NodeInfo &GetFirstNode() const;
/** Function returns the node info of the last path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
NodeInfo &GetLastNode();
/** Function returns the node info of the last path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
const NodeInfo &GetLastNode() const;
const NodeInfoVectorType &GetNodes() const;
/**Compares two PropertyKeyPaths for real equality. So it is a string comparison of their string conversion.*/
bool operator==(const PropertyKeyPath &path) const;
/**Operation equals like comparing the ToStr() results with operator <.*/
bool operator<(const PropertyKeyPath &right) const;
/**Operation equals like comparing the ToStr() results with operator <=.*/
bool operator<=(const PropertyKeyPath &right) const;
/**Operation equals like comparing the ToStr() results with operator >=.*/
bool operator>=(const PropertyKeyPath &right) const;
/**Operation equals like comparing the ToStr() results with operator >.*/
bool operator>(const PropertyKeyPath &right) const;
/**Checks if two PropertyKeyPaths specify the same node. Hence all wildcards will be processed.\n
* E.G.: "item1.child1.grandChild2" == "item1.*.grandChild2" is true.
* \remark If you want to check if two paths are "truly" equal and not only equal in terms of
* pointing to the same node, use the member function operator ==().*/
bool Equals(const PropertyKeyPath &path) const;
PropertyKeyPath &operator=(const PropertyKeyPath &path);
/** Appends an "any element" to the path instance.*/
PropertyKeyPath &AddAnyElement();
/** Appends an element with the passed name to the path instance.*/
PropertyKeyPath &AddElement(const ElementNameType &name);
/** Appends an element with the passed name and any selection to the path instance.*/
PropertyKeyPath &AddAnySelection(const ElementNameType &name);
/** Appends an element with the passed name and selection index to the path instance.*/
PropertyKeyPath &AddSelection(const ElementNameType &name, ItemSelectionIndex index);
PropertyKeyPath();
PropertyKeyPath(const PropertyKeyPath &path);
/** overload constructor that supports simple key paths consisting only of elements.*/
PropertyKeyPath(const std::initializer_list< ElementNameType >& list);
~PropertyKeyPath();
void Reset();
protected:
NodeInfoVectorType m_NodeInfos;
static bool PropertyKeyPathsMatch(const PropertyKeyPath &left, const PropertyKeyPath &right);
};
class MITKCORE_EXPORT InvalidPathNodeException : public mitk::Exception
{
public:
mitkExceptionClassMacro(InvalidPathNodeException, mitk::Exception);
};
MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const PropertyKeyPath &path);
/**Helper function that converts a path PropertyKeyPath into a regex string that can be used
to search for property keys (using std::regex) that are matched by the PropertyKeyPath.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyRegEx(const PropertyKeyPath &tagPath);
/**Helper function that converts a path PropertyKeyPath into a regex string that can be used
to search for property persistence keys (using std::regex) that are matched by the PropertyKeyPath.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyRegEx(const PropertyKeyPath &tagPath);
/**Helper function that converts a path PropertyKeyPath into a regex that can be used as key template
in a PropertyPersistanceInfo.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyTemplate(const PropertyKeyPath &tagPath);
/**Helper function that converts a path PropertyKeyPath into a regex that can be used as name template
in a PropertyPersistanceInfo.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceNameTemplate(const PropertyKeyPath &tagPath);
/** Converts the passed property name into a tag path. If the property name cannot be converted
into a valid path, the returned path is empty.*/
MITKCORE_EXPORT PropertyKeyPath PropertyNameToPropertyKeyPath(const std::string &propertyName);
/** returns the correct property name for a given PropertyKeyPath instance. */
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyName(const PropertyKeyPath &tagPath);
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h
index 1a77b47f64..e2c43b9018 100644
--- a/Modules/Core/include/mitkPropertyRelationRuleBase.h
+++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h
@@ -1,403 +1,403 @@
/*============================================================================
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 mitkPropertyRelationRuleBase_h
#define mitkPropertyRelationRuleBase_h
#include "mitkIPropertyOwner.h"
#include "mitkIdentifiable.h"
#include "mitkException.h"
#include "mitkNodePredicateBase.h"
#include "mitkPropertyKeyPath.h"
#include <MitkCoreExports.h>
#include <string>
namespace mitk
{
/**Base class to standardize/abstract/encapsulate rules and business logic to detect and define
(property/data based) relations in MITK.
Following important definitions must be regarded when using/implementing/specifying rule classes:
- Relations represented by rules are directed relations that point from a source IPropertyOwner (Source)
to a destination IPropertyOwner (Destination).
- Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations.
Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules,
abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule:
in its abstract state it cannot connect but be used to detect any type of generic ID relation.
- A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the
way to express the source image relation to an input image and to a mask would be nearly the same
and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual
stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for
"DICOM source mask" and both rules must have distinct rule IDs.
- Source may have several relations of a rule to different Destinations.
Destination may have several relations of a rule from different Sources. But a specific source destination
pair may have only one relation of a specific rule id (concrete rule). A specific source destination
pair may however have multiple relations for an abstract rule.
- The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation
but it should still be documented, even if the destination is unknown. One has to explicitly
disconnect a zombie relation to get rid of it.
- Each relation has its own UID (relationUID) that can be used to address it.
The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer
which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer
which uses the properties of Source and Destination to deduce if there is a relation of the rule type.
The ID-layer is completely implemented by this base class. The base class falls back to the Data-layer
(implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated to (only)
look at the data layer.
Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are
possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was
connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction.
Rules use relation instance identifying (RII) properties in order to manage their relations that are stored in the
Source. The RII-properties follow the following naming schema:
"MITK.Relations.\<InstanceID\>.[relationUID|destinationUID|ruleID|\<data-layer-specific\>]"
- \<InstanceID\>: The unique index of the relation for the Source. Used to assign/group the properties to
their relation. In the default implementation of this class the instance id is an positive integer (i>0).
- relationUID: The UID of the relation. Set by the ID-layer (so by this class)
- destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements
IIdentifiable.
- ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set
automatically be this base class.
- <data-layer-specific>: Information needed by the Data-layer (so derived classes) to find the relationUID
*/
class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object
{
public:
mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object);
itkCloneMacro(Self);
itkCreateAnotherMacro(Self);
using RuleIDType = std::string;
using RelationUIDType = Identifiable::UIDType;
using RelationUIDVectorType = std::vector<RelationUIDType>;
/** Enum class for different types of relations. */
enum class RelationType
{
None = 0, /**< Two IPropertyOwner have no relation under the rule.*/
Data = 1, /**< Two IPropertyOwner have a relation, but it is "only" deduced from the Data-layer (so a bit
"weaker" as ID). Reasons for the missing ID connection could be that Destintination has not
IIdentifiable implemented.*/
ID = 2, /**< Two IPropertyOwner have a relation and are explicitly connected via the ID of IIdentifiable of the Destination.*/
Complete = 3 /**< Two IPropertyOwner have a relation and are fully explicitly connected (via data layer and ID layer).*/
};
using RelationVectorType = std::vector<RelationType>;
/** Returns the generic root path for relation rules ("MITK.Relations").*/
static PropertyKeyPath GetRootKeyPath();
using InstanceIDType = std::string;
/** Returns the property key path for a RII property.
* @param propName If not empty a PropertyPath element will added (with the passed value) after the \<InstanceID\> element.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RIIs property of any instance.*/
static PropertyKeyPath GetRIIPropertyKeyPath(const std::string propName, const InstanceIDType& instanceID);
/** Returns the property key path for RII RelationUID properties.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RII RelationUIDs property of any instance.*/
static PropertyKeyPath GetRIIRelationUIDPropertyKeyPath(const InstanceIDType& instanceID = "");
/** Returns the property key path for RII RuleID properties.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RII RuleIDs property of any instance.*/
static PropertyKeyPath GetRIIRuleIDPropertyKeyPath(const InstanceIDType& instanceID = "");
/** Returns the property key path for RII DestinationUID properties.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RII DestinationUIDs property of any instance.*/
static PropertyKeyPath GetRIIDestinationUIDPropertyKeyPath(const InstanceIDType& instanceID = "");
/** Returns an ID string that identifies the rule class.
@post The returned rule ID must met the preconditions of a PropertyKeyPath element name
(see mitk::PropertyKeyPath*/
virtual RuleIDType GetRuleID() const = 0;
/** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/
virtual std::string GetDisplayName() const = 0;
/** Returns a human readable string that can be used to describe the role of a source in context of the rule
* instance.*/
virtual std::string GetSourceRoleName() const = 0;
/** Returns a human readable string that can be used to describe the role of a destination in context of the rule
* instance.*/
virtual std::string GetDestinationRoleName() const = 0;
/** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement
if another behavior is needed.*/
virtual bool IsAbstract() const;
/** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a
True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if
they have requirements on potential Sources).*/
virtual bool IsSourceCandidate(const IPropertyProvider *owner) const;
/** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a
True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if
they have requirements on potential Sources).*/
virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const;
/** Returns true if the passed owner is a Source of a relation defined by the rule.
@pre owner must be a pointer to a valid IPropertyProvider instance.*/
bool IsSource(const IPropertyProvider *owner) const;
/** Returns all relation types of the passed IPropertyOwner instances.
@remark Abstract rules may have several relationtypes between the instances (from different supported concrete rules), that cover
both ID and Data relations; thus it returns a vector of RelationTypes.
@result Vector of all relation types that exist between the given instances. Empty vector equals none relation at all.
@pre source must be a pointer to a valid IPropertyProvider instance.
@pre destination must be a pointer to a valid IPropertyProvider instance.
*/
RelationVectorType GetRelationTypes(const IPropertyProvider* source, const IPropertyProvider* destination) const;
/** Indicates if passed IPropertyOwner instances have a relation of a certain type.
@remark Abstract rules may also indicate RelationType::Complete if there
are multiple relations (from different supported concrete rules), that cover
both ID and Data relations.
@param source
@param destination
@param requiredRelation Defines the type of relation that should be present. None: does not matter which one, as long as at least one is present.
Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.
@pre source must be a pointer to a valid IPropertyProvider instance.
@pre destination must be a pointer to a valid IPropertyProvider instance.
*/
bool HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination, RelationType requiredRelation = RelationType::None) const;
/** Returns a vector of relation UIDs for all relations of this rule instance that are defined for
the passed source.
@pre source must be a pointer to a valid IPropertyOwner instance.
@param source
@param layer Defines the layer the relations must be reflected. None: does not matter which one, as long as at least one is present.
Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.*/
RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source, RelationType layer = RelationType::None) const;
/** Returns the relation UID(s) for the passed source and destination of this rule instance.
If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only
one relation UID.
@pre source must be a pointer to a valid IPropertyOwner instance.
@pre destination must be a pointer to a valid IPropertyOwner instance.*/
RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const;
/** Returns the relation UID for the passed source and destination of this rule instance.
If the passed instances have no relation, no ID can be deduced and an exception will be thrown.
If more than one relation is found, also an exception will be thrown. Thus only use this convenience method,
if you are sure that one(!) relation UID can exist.
@pre source must be a pointer to a valid IPropertyOwner instance.
@pre destination must be a pointer to a valid IPropertyOwner instance.
@pre Source and destination have one relation; otherwise
if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists
a default MITK exception is thrown.*/
RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const;
/**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet).
Thus all nodes where IsSourceCandidate() returns true. */
NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const;
/**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination
yet). Thus all nodes where IsDestinationCandidate() returns true. */
NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const;
/**Predicate that can be used to find nodes that are Sources of that rule and connected.
Thus all nodes where IsSource() returns true.*/
NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const;
/**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule
@param destination Pointer to the Destination instance that should be used for detection.
@param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default);
Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations.
@pre Destination must be a valid instance.*/
NodePredicateBase::ConstPointer GetSourcesDetector(
const IPropertyProvider *destination, RelationType exclusiveRelation = RelationType::None) const;
/**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule
@param source Pointer to the Source instance that should be used for detection.
@param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default);
Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations.
@pre Source must be a valid instance.*/
NodePredicateBase::ConstPointer GetDestinationsDetector(
const IPropertyProvider *source, RelationType exclusiveRelation = RelationType::None) const;
/**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID.
@param source Pointer to the Source instance that should be used for detection.
@param relationUID
@pre source must be a valid instance.
@pre relationUID must identify a relation of the passed source and rule. (This must be in the return of
this->GetExistingRelations(source). */
NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source,
RelationUIDType relationUID) const;
/**Disconnect the passed instances by modifying source. One can specify which layer should be disconnected
via the argument "layer". Default is the complete disconnection.
All RII-properties or properties that define the connection on the data layer in the source
for the passed destination will be removed.
@pre source must be a valid instance.
@pre destination must be a valid instance.
@param source
@param destination
@param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection
on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/
void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer = RelationType::Complete) const;
/**Disconnect the source from the passed relationUID (useful for "zombie relations").
One can specify which layer should be disconnected
via the argument "layer". Default is the complete disconnection.
All RII-properties or properties that define the connection on the data layer in the source
for the passed destination will be removed.
If the relationUID is not part of the source. Nothing will be changed.
@pre source must be a valid instance.
@param source
@param relationUID
@param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection
on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/
void Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer = RelationType::Complete) const;
/**Returns the list of PropertyKeyPaths of all properties that are relevant for a given relation.
@param source Pointer to the Source instance that contains the potential properties.
@param relationUID UID of the relation that is relevant for the requested properties.
@param layer Indicates which layer is requested. ID: returns all RII properties that belong to the relation. Data: returns all properties that are relevant/belong to the data layer of the relation. Complete: returns all properties (ID+Data)
@pre source must be a valid instance.
@pre relationUID must identify a relation of the passed source and rule. (This must be in the return of
this->GetExistingRelations(source). */
std::vector<PropertyKeyPath> GetRelationPropertyPaths(const IPropertyProvider* source,
RelationUIDType relationUID, RelationType layer = RelationType::Data) const;
protected:
PropertyRelationRuleBase() = default;
~PropertyRelationRuleBase() override = default;
using InstanceIDVectorType = std::vector<InstanceIDType>;
static InstanceIDType NULL_INSTANCE_ID();
/** Returns the instance IDs for the passed source and destination for this rule instance.
If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID),
an empty vector will be returned.
@remark Per definition of property relation rules only 0 or 1 instance should be found for one provider
pair and concrete rule. But there might be more then one instanceID because either 1) the rule is abstract and
supports multiple rule IDs or 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance
could match. The implementation of this function should report all relation instances. The calling function
will take care.
@pre source must be a pointer to a valid IPropertyProvider instance.
@pre destination must be a pointer to a valid IPropertyProvider instance.*/
InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source,
const IPropertyProvider *destination) const;
using DataRelationUIDVectorType = std::vector< std::pair<RelationUIDType, RuleIDType> >;
/** Returns the RelationUIDs of all relations that are defined by the data layer of source for
this rule instance and, if defined, destination.
If the passed source (and destination) instance has no relation on the data layer,
an empty vector will be returned.
@remark Per definition for property relation rules only 0 or 1 instance should be found for one provider
pair and concrete rule. But there might be more then one instance because either 1) the rule is abstract and
supports multiple rule IDs or 2) the data layer may be ambiguous (e.g. because the destination was not specified)
and therefore multiple relation instances of the rule instance could match.
The implementation of this function should report all relation instances. The calling function
will take care.
@param source
@param destination Destination the find relations should point to. If destination is NULL any relation
on the data layer for this rule and source are wanted.
@param instances_IDLayer List of relation instances that are already defined by the ID layer. The implementation of this
function should only cover relations that are not already resembled in the passed relarions_IDLayer.
@pre source must be a pointer to a valid IPropertyProvider instance.*/
virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider * source,
const IPropertyProvider * destination, const InstanceIDVectorType& instances_IDLayer) const = 0;
/**Helper function that deduces the relation UID of the given relation instance.
If it cannot be deduced an NoPropertyRelationException is thrown.*/
RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const;
/**Helper function that deduces the relation instance ID given the relation UID.
If it cannot be deduced an NoPropertyRelationException is thrown.*/
InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source,
const RelationUIDType &relationUID) const;
/**Explicitly connects the passed instances. Afterwards they have a relation of Data (if data layer is supported), ID (if a
destination implements IIdentifiable) or Complete (if Data and ID could be connected). If the passed instance are already
connected the old connection will be overwritten (and raised to the highest possible connection level).
@remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes
which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations.
@pre source must be a valid instance.
@pre destination must be a valid instance.
@pre the rule instance must not be abstract.
@return Return the relation uid of the relation connected by this method call*/
RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const;
/**Is called by Connect() to ensure that source has correctly set properties to resemble
the relation on the data layer. This means that the method should set the properties that describe
and encode the relation on the data layer (data-layer-specific relation properties).
If the passed instance are already connected, the old settings should be
overwritten. Connect() will ensure that source and destination are valid pointers.
@param source
@param destination
@param instanceID is the ID for the relation instance that should be connected. Existence of the relation instance
is ensured.
@pre source must be a valid instance.
@pre destination must be a valid instance.*/
virtual void Connect_datalayer(IPropertyOwner *source,
const IPropertyProvider *destination,
const InstanceIDType &instanceID) const = 0;
/**This method is called by Disconnect() to remove all properties of the relation from the source that
are set by Connect_datalayer().
@remark This method should remove all properties that are set for a specific relation by Connect_datalayer(...).
If the relationUID is not part of the source, nothing will be changed. Disconnect() ensures that source is a valid
pointer if called.
- @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/
+ @remark Disconnect() ensures that source is valid and only invokes if instance exists.*/
virtual void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const = 0;
/** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because
it is an abstract rule which also supports the rule ID.
@return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/
virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const;
/** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance.
* @param propName If not empty a PropertyPath element will be added (with the passed value) after the \<InstanceID\> element.
* @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/
std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const;
/**Helper function that deduces the instance ID out of a property name.
If it cannot be deduced an MITK exception is thrown.*/
static InstanceIDType GetInstanceIDByPropertyName(const std::string propName);
/**Helper function that retrieves the rule ID of a relation instance of a passed source.
@pre source must be valid.
@pre source must have a relation instance with this ID*/
RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source,
const InstanceIDType &instanceID) const;
/**Helper function that retrieves the destination UID of a relation instance of a passed
source. If the relation has no destination UID, an empty string will be returned.
@pre source must be valid.*/
std::string GetDestinationUIDByInstanceID(const IPropertyProvider * source,
const InstanceIDType & instanceID) const;
itk::LightObject::Pointer InternalClone() const override;
/** helper method that serves as a workaround until T24729 is done.
Please remove if T24728 is done then could directly use owner->GetPropertyKeys() again.*/
static std::vector<std::string> GetPropertyKeys(const IPropertyProvider *owner);
/** Helper method that tries to cast the provider to the Identifiable interface.*/
const Identifiable* CastProviderAsIdentifiable(const mitk::IPropertyProvider* provider) const;
private:
/** Creates a relation UID*/
static RelationUIDType CreateRelationUID();
/**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be generated
and a relationUID property with the relationUID will be set to block the instance ID. The
instance ID will be returned.
@remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used
concurrently.*/
InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const;
};
/**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/
class NoPropertyRelationException : public Exception
{
public:
mitkExceptionClassMacro(NoPropertyRelationException, Exception)
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkRenderingManager.h b/Modules/Core/include/mitkRenderingManager.h
index 5dab86b41a..2e5c3a4775 100644
--- a/Modules/Core/include/mitkRenderingManager.h
+++ b/Modules/Core/include/mitkRenderingManager.h
@@ -1,503 +1,503 @@
/*============================================================================
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 mitkRenderingManager_h
#define mitkRenderingManager_h
#include <MitkCoreExports.h>
#include <vtkCallbackCommand.h>
#include <itkObject.h>
#include <itkObjectFactory.h>
#include <mitkProperties.h>
#include <mitkPropertyList.h>
#include <mitkTimeGeometry.h>
#include <mitkAntiAliasing.h>
class vtkRenderWindow;
class vtkObject;
namespace mitk
{
class RenderingManagerFactory;
class BaseGeometry;
class TimeNavigationController;
class BaseRenderer;
class DataStorage;
/**
* \brief Manager for coordinating the rendering process.
*
* RenderingManager is a central instance retrieving and executing
* RenderWindow update requests. Its main purpose is to coordinate
* distributed requests which cannot be aware of each other - lacking the
* knowledge of whether they are really necessary or not. For example, two
* objects might determine that a specific RenderWindow needs to be updated.
* This would result in one unnecessary update, if both executed the update
* on their own.
*
* The RenderingManager addresses this by letting each such object
* <em>request</em> an update, and waiting for other objects to possibly
* issue the same request. The actual update will then only be executed at a
* well-defined point in the main event loop (this may be each time after
* event processing is done).
*
* Convenience methods for updating all RenderWindows which have been
* registered with the RenderingManager exist. If these methods are not
* used, it is not required to register (add) RenderWindows prior to using
* the RenderingManager.
*
* The methods #ForceImmediateUpdate() and #ForceImmediateUpdateAll() can
* be used to force the RenderWindow update execution without any delay,
* bypassing the request functionality.
*
* The interface of RenderingManager is platform independent. Platform
* specific subclasses have to be implemented, though, to supply an
* appropriate event issuing for controlling the update execution process.
* See method documentation for a description of how this can be done.
*
* \sa TestingRenderingManager An "empty" RenderingManager implementation which
* can be used in tests etc.
*
*/
class MITKCORE_EXPORT RenderingManager : public itk::Object
{
public:
mitkClassMacroItkParent(RenderingManager, itk::Object);
typedef std::vector<vtkRenderWindow *> RenderWindowVector;
typedef std::vector<float> FloatVector;
typedef std::vector<bool> BoolVector;
typedef itk::SmartPointer<DataStorage> DataStoragePointer;
enum RequestType
{
REQUEST_UPDATE_ALL = 0,
REQUEST_UPDATE_2DWINDOWS,
REQUEST_UPDATE_3DWINDOWS
};
static Pointer New();
/** Set the object factory which produces the desired platform specific
* RenderingManager singleton instance. */
static void SetFactory(RenderingManagerFactory *factory);
/** Get the object factory which produces the platform specific
* RenderingManager instances. */
static const RenderingManagerFactory *GetFactory();
/** Returns true if a factory has already been set. */
static bool HasFactory();
/** Get the RenderingManager singleton instance. */
static RenderingManager *GetInstance();
/** Returns true if the singleton instance does already exist. */
static bool IsInstantiated();
/** Adds a RenderWindow. This is required if the methods #RequestUpdateAll
* or #ForceImmediateUpdate are to be used. */
void AddRenderWindow(vtkRenderWindow *renderWindow);
/** Removes a RenderWindow. */
void RemoveRenderWindow(vtkRenderWindow *renderWindow);
/** Get a list of all registered RenderWindows */
const RenderWindowVector &GetAllRegisteredRenderWindows();
/** Requests an update for the specified RenderWindow, to be executed as
* soon as the main loop is ready for rendering. */
void RequestUpdate(vtkRenderWindow *renderWindow);
/** Immediately executes an update of the specified RenderWindow. */
void ForceImmediateUpdate(vtkRenderWindow *renderWindow);
/** Requests all currently registered RenderWindows to be updated.
* If only 2D or 3D windows should be updated, this can be specified
* via the parameter requestType. */
void RequestUpdateAll(RequestType type = REQUEST_UPDATE_ALL);
/** Immediately executes an update of all registered RenderWindows.
* If only 2D or 3D windows should be updated, this can be specified
* via the parameter requestType. */
void ForceImmediateUpdateAll(RequestType type = REQUEST_UPDATE_ALL);
/**
* @brief Initialize the render windows by the aggregated geometry of all objects that are held in
* the data storage.
*
* @param dataStorage The data storage from which the bounding object can be retrieved
*/
virtual void InitializeViewsByBoundingObjects(const DataStorage* dataStorage);
/**
* @brief Initialize the given render window by the aggregated geometry of all objects that are held in
* the data storage.
*
- * @param renderWindow The specifid render window to update
+ * @param renderWindow The specified render window to update
* @param dataStorage The data storage from which the bounding object can be retrieved
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual void InitializeViewByBoundingObjects(vtkRenderWindow* renderWindow,
const DataStorage* dataStorage,
bool resetCamera = true);
/**
* @brief Initialize the render windows specified by "type" to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param geometry The geometry to be used to initialize / update a
* render window's time and slice navigation controller
* @param type The type of update request:
* - REQUEST_UPDATE_ALL will initialize / update the
* time and slice navigation controller of all retrieved render windows
* - REQUEST_UPDATE_2DWINDOWS will only initialize / update the
* time and slice navigation controller of 2D render windows
* - REQUEST_UPDATE_3DWINDOWS will only initialize / update the
* time and slice navigation controller of 3D render windows
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeViews(const BaseGeometry *geometry,
RequestType type = REQUEST_UPDATE_ALL,
bool resetCamera = true);
/**
* @brief Initialize the render windows specified by "type" to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param geometry The geometry to be used to initialize / update a
* render window's time- and slice navigation controller
* @param type The type of update request:
* - REQUEST_UPDATE_ALL will initialize / update the
* time- and slice navigation controller of all retrieved render windows
* - REQUEST_UPDATE_2DWINDOWS will only initialize / update the
* time- and slice navigation controller of 2D render windows
* - REQUEST_UPDATE_3DWINDOWS will only initialize / update the
* time- and slice navigation controller of 3D render windows
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeViews(const TimeGeometry *geometry,
RequestType type = REQUEST_UPDATE_ALL,
bool resetCamera = true);
/**
* @brief Initialize the render windows specified by "type" to the default viewing direction
* without updating the geometry information.
*
* @param type The type of update request:
* - REQUEST_UPDATE_ALL will initialize the
* slice navigation controller of all retrieved render windows
* - REQUEST_UPDATE_2DWINDOWS will only initialize the
* slice navigation controller of 2D render windows
* - REQUEST_UPDATE_3DWINDOWS will only initialize the
* slice navigation controller of 3D render windows
*/
virtual bool InitializeViews(RequestType type = REQUEST_UPDATE_ALL);
/**
* @brief Initialize the specified render window to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param renderWindow The specific render window to update
* @param geometry The geometry to be used to initialize / update the
* render window's time- and slice navigation controller
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow,
const BaseGeometry *geometry,
bool resetCamera = true);
/**
* @brief Initialize the specified render window to the given geometry.
*
* Throws an exception if bounding box has 0 extent due to exceeding double precision range.
*
* @param renderWindow The specific render window to update
* @param geometry The geometry to be used to initialize / update the
* render window's time- and slice navigation controller
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow,
const TimeGeometry *geometry,
bool resetCamera = true);
/**
* @brief Initialize the specified render window to the default viewing direction
* without updating the geometry information.
*
* @param renderWindow The specific render window to update
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow);
/** Gets the (global) TimeNavigationController responsible for
* time-slicing. */
const TimeNavigationController* GetTimeNavigationController() const;
/** Gets the (global) TimeNavigationController responsible for
* time-slicing. */
TimeNavigationController* GetTimeNavigationController();
~RenderingManager() override;
/** Executes all pending requests. This method has to be called by the
* system whenever a RenderingManager induced request event occurs in
* the system pipeline (see concrete RenderingManager implementations). */
virtual void ExecutePendingRequests();
bool IsRendering() const;
void AbortRendering();
/** En-/Disable LOD increase globally. */
itkSetMacro(LODIncreaseBlocked, bool);
/** En-/Disable LOD increase globally. */
itkGetMacro(LODIncreaseBlocked, bool);
/** En-/Disable LOD increase globally. */
itkBooleanMacro(LODIncreaseBlocked);
/** En-/Disable LOD abort mechanism. */
itkSetMacro(LODAbortMechanismEnabled, bool);
/** En-/Disable LOD abort mechanism. */
itkGetMacro(LODAbortMechanismEnabled, bool);
/** En-/Disable LOD abort mechanism. */
itkBooleanMacro(LODAbortMechanismEnabled);
/** Force a sub-class to start a timer for a pending hires-rendering request */
virtual void StartOrResetTimer(){};
/** To be called by a sub-class from a timer callback */
void ExecutePendingHighResRenderingRequest();
virtual void DoStartRendering(){};
virtual void DoMonitorRendering(){};
virtual void DoFinishAbortRendering(){};
int GetNextLOD(BaseRenderer *renderer);
/** Set current LOD (nullptr means all renderers)*/
void SetMaximumLOD(unsigned int max);
void SetShading(bool state, unsigned int lod);
bool GetShading(unsigned int lod);
void SetClippingPlaneStatus(bool status);
bool GetClippingPlaneStatus();
void SetShadingValues(float ambient, float diffuse, float specular, float specpower);
FloatVector &GetShadingValues();
/** Returns a property list */
PropertyList::Pointer GetPropertyList() const;
/** Returns a property from m_PropertyList */
BaseProperty *GetProperty(const char *propertyKey) const;
/** Sets or adds (if not present) a property in m_PropertyList */
void SetProperty(const char *propertyKey, BaseProperty *propertyValue);
/**
* \brief Setter for internal DataStorage
*
* Sets the DataStorage that is used internally. This instance holds all DataNodes that are
* rendered by the registered BaseRenderers.
*
* If this DataStorage is changed at runtime by calling SetDataStorage(),
* all currently registered BaseRenderers are automatically given the correct instance.
* When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage.
*/
void SetDataStorage(DataStorage *storage);
/**
* \brief Getter for internal DataStorage
*
* Returns the DataStorage that is used internally. This instance holds all DataNodes that are
* rendered by the registered BaseRenderers.
*/
itkGetMacro(DataStorage, DataStorage*);
itkGetConstMacro(DataStorage, DataStorage*);
/**
* @brief Sets a flag to the given renderwindow to indicated that it has the focus e.g. has been clicked recently.
* @param focusWindow
*/
void SetRenderWindowFocus(vtkRenderWindow *focusWindow);
itkGetMacro(FocusedRenderWindow, vtkRenderWindow *);
itkSetMacro(ConstrainedPanningZooming, bool);
itkGetConstMacro(ConstrainedPanningZooming, bool);
void SetAntiAliasing(AntiAliasing antiAliasing);
itkGetConstMacro(AntiAliasing, AntiAliasing);
protected:
enum
{
RENDERING_INACTIVE = 0,
RENDERING_REQUESTED,
RENDERING_INPROGRESS
};
RenderingManager();
/** Abstract method for generating a system specific event for rendering
* request. This method is called whenever an update is requested */
virtual void GenerateRenderingRequestEvent() = 0;
virtual void InitializePropertyList();
bool m_UpdatePending;
typedef std::map<BaseRenderer *, unsigned int> RendererIntMap;
typedef std::map<BaseRenderer *, bool> RendererBoolMap;
RendererBoolMap m_RenderingAbortedMap;
RendererIntMap m_NextLODMap;
unsigned int m_MaxLOD;
bool m_LODIncreaseBlocked;
bool m_LODAbortMechanismEnabled;
BoolVector m_ShadingEnabled;
bool m_ClippingPlaneEnabled;
FloatVector m_ShadingValues;
static void RenderingStartCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata);
static void RenderingProgressCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata);
static void RenderingEndCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata);
typedef std::map<vtkRenderWindow *, int> RenderWindowList;
RenderWindowList m_RenderWindowList;
RenderWindowVector m_AllRenderWindows;
struct RenderWindowCallbacks
{
vtkCallbackCommand *commands[3u];
};
typedef std::map<vtkRenderWindow *, RenderWindowCallbacks> RenderWindowCallbacksList;
RenderWindowCallbacksList m_RenderWindowCallbacksList;
itk::SmartPointer<TimeNavigationController> m_TimeNavigationController;
static RenderingManager::Pointer s_Instance;
static RenderingManagerFactory *s_RenderingManagerFactory;
PropertyList::Pointer m_PropertyList;
DataStoragePointer m_DataStorage;
bool m_ConstrainedPanningZooming;
private:
/**
* @brief Initialize the specified renderer to the given geometry.
*
* @param baseRenderer The specific renderer to update
* @param geometry The geometry to be used to initialize / update the
* render window's slice navigation controller
* @param boundingBoxInitialized If this parameter is set to true, the slice navigation controller will be
* initialized / updated with the given geometry. If set to false, the geometry
* of the slice navigation controller is not updated.
* @param mapperID The mapper ID is used to define if the given renderer is a 2D or a 3D renderer.
* In case of a 2D renderer and if "boundingBoxInitialized" is set to true (slice
* navigation controller will be updated with a new geometry), the position of the
* slice navigation controller is set to the center slice.
* @param resetCamera If this parameter is set to true, the camera controller will be
* set / fit to the center of the rendered image. If set to false, only the
* the slice navigation controller is reset to the geometry without changing
* the camera view / position.
*/
void InternalViewInitialization(BaseRenderer *baseRenderer,
const TimeGeometry *geometry,
bool boundingBoxInitialized,
int mapperID,
bool resetCamera);
/**
* @brief Extend the bounding box of the given geometry to make sure the bounding box has an extent bigger than
* zero in any direction.
*
* @param originalGeometry The original geometry to be extended
* @param modifiedGeometry The modified geometry where the new bounds (extended bounding box) are used / set
*/
bool ExtendGeometryForBoundingBox(const TimeGeometry* originalGeometry, TimeGeometry::Pointer& modifiedGeometry);
vtkRenderWindow *m_FocusedRenderWindow;
AntiAliasing m_AntiAliasing;
};
#pragma GCC visibility push(default)
itkEventMacroDeclaration(RenderingManagerEvent, itk::AnyEvent);
itkEventMacroDeclaration(RenderingManagerViewsInitializedEvent, RenderingManagerEvent);
#pragma GCC visibility pop
itkEventMacroDeclaration(FocusChangedEvent, itk::AnyEvent);
/**
* Generic RenderingManager implementation for "non-rendering-platform",
* e.g. for tests. Its factory (TestingRenderingManagerFactory) is
* automatically on start-up and is used by default if not other
* RenderingManagerFactory is instantiated explicitly thereafter.
* (see mitkRenderingManager.cpp)
*/
class MITKCORE_EXPORT TestingRenderingManager : public RenderingManager
{
public:
mitkClassMacro(TestingRenderingManager, RenderingManager);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
protected:
void GenerateRenderingRequestEvent() override {};
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkStatusBarImplementation.h b/Modules/Core/include/mitkStatusBarImplementation.h
index 5324621fcb..6aa47bfdf1 100644
--- a/Modules/Core/include/mitkStatusBarImplementation.h
+++ b/Modules/Core/include/mitkStatusBarImplementation.h
@@ -1,60 +1,60 @@
/*============================================================================
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 mitkStatusBarImplementation_h
#define mitkStatusBarImplementation_h
#include <MitkCoreExports.h>
#include <mitkCommon.h>
namespace mitk
{
//##Documentation
- //## @brief GUI independent Interface for all Gui depentent implementations of a StatusBar.
+ //## @brief GUI independent Interface for all Gui dependent implementations of a StatusBar.
class MITKCORE_EXPORT StatusBarImplementation
{
public:
mitkClassMacroNoParent(StatusBarImplementation)
//##Documentation
//## @brief Constructor
StatusBarImplementation(){};
//##Documentation
//## @brief Destructor
virtual ~StatusBarImplementation(){};
//##Documentation
//## @brief Send a string to the applications StatusBar
virtual void DisplayText(const char *t) = 0;
//##Documentation
//## @brief Send a string with a time delay to the applications StatusBar
virtual void DisplayText(const char *t, int ms) = 0;
virtual void DisplayErrorText(const char *t) = 0;
virtual void DisplayWarningText(const char *t) = 0;
virtual void DisplayWarningText(const char *t, int ms) = 0;
virtual void DisplayGenericOutputText(const char *t) = 0;
virtual void DisplayDebugText(const char *t) = 0;
virtual void DisplayGreyValueText(const char *t) = 0;
//##Documentation
//## @brief removes any temporary message being shown.
virtual void Clear() = 0;
//##Documentation
//## @brief Set the SizeGrip of the window
//## (the triangle in the lower right Windowcorner for changing the size)
//## to enabled or disabled
virtual void SetSizeGripEnabled(bool enable) = 0;
};
} // end namespace mitk
#endif
diff --git a/Modules/Core/include/mitkTimeNavigationController.h b/Modules/Core/include/mitkTimeNavigationController.h
index 16128e2106..4ded3fcc28 100644
--- a/Modules/Core/include/mitkTimeNavigationController.h
+++ b/Modules/Core/include/mitkTimeNavigationController.h
@@ -1,172 +1,172 @@
/*============================================================================
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 TimeNavigationController_h
#define TimeNavigationController_h
#include <MitkCoreExports.h>
#include <mitkBaseController.h>
#include <mitkTimeGeometry.h>
#include <itkCommand.h>
namespace mitk
{
/**
* \brief Controls the time-related properties of the time stepper, according to the
- * given input world time geomtry.
+ * given input world time geometry.
*
* A TimeNavigationController takes a TimeGeometry as input world time geometry
* and sets the properties of the associated stepper (BaseController).
* The time-related properties are:
* - steps: the number of input time steps
* - pos: the current time step / position of the stepper
* - range: the time bounds for the slider (minimum, maximum)
*
* The timestep controls the selection of a specific time point from the TimeGeometry.
* The TimeNavigationController generates ITK events to tell observers,
* like a BaseRenderer, when the selected timestep changes.
*
* Example:
* \code
* // Initialize the time navigation controller.
* timeCtrl = mitk::TimeNavigationController::New();
*
- * // Set the required input world time geomtry (a TimeGeometry::ConstPointer).
+ * // Set the required input world time geometry (a TimeGeometry::ConstPointer).
* timeCtrl->SetInputWorldTimeGeometry(geometry.GetPointer());
*
* // Set the time-related properties and send the information to the connected renderer(s).
* timeCtrl->Update();
* \endcode
*
* Vvisible navigation widgets can be connected to a TimeNavigationController, e.g., a
* QmitkSliceNavigationWidget (for Qt):
*
* \code
* // Create the visible navigation widget (a slider with a spin-box).
* QmitkSliceNavigationWidget* navigationWidget = new QmitkSliceNavigationWidget(parent);
*
* // Connect the navigation widget to the time-stepper of the
* // TimeNavigationController. For initialization (timestep, minimal and
* // maximal values) the values of the TimeNavigationController are used.
* // Thus, accessing methods of a navigation widget is normally not necessary,
* // since everything can be set via the (Qt-independent) TimeNavigationController.
* // The QmitkStepperAdapter converts the Qt-signals to Qt-independent
* // itk-events.
* new QmitkStepperAdapter(navigationWidget, timeCtrl->GetStepper());
* \endcode
*/
class MITKCORE_EXPORT TimeNavigationController : public BaseController
{
public:
mitkClassMacro(TimeNavigationController, BaseController);
itkNewMacro(Self);
/**
* \brief Set the input time geometry out of which the
* time-related properties will be generated.
*
* Any previous set input geometry (3D or Time) will
* be ignored in the future.
*/
void SetInputWorldTimeGeometry(const TimeGeometry* geometry);
itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry);
/**
* \brief Do the actual time-related properties extraction
- and send the currently selected time step to the connecte
+ and send the currently selected time step to the connected
observers (renderers).
*/
virtual void Update();
/**
* \brief Send the currently selected time step to the connected
* observers (renderers).
*
* Called by Update().
*/
virtual void SendTime();
class MITKCORE_EXPORT TimeEvent : public itk::AnyEvent
{
public:
typedef TimeEvent Self;
typedef itk::AnyEvent Superclass;
TimeEvent(TimeStepType timeStep) : m_TimeStep(timeStep) {}
~TimeEvent() override {}
const char *GetEventName() const override { return "TimeEvent"; }
bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); }
::itk::EventObject *MakeObject() const override { return new Self(m_TimeStep); }
TimeStepType GetTimeStep() const { return m_TimeStep; }
private:
TimeStepType m_TimeStep;
void operator=(const Self &);
};
template <typename T>
void ConnectTimeEvent(T* receiver)
{
typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime);
unsigned long tag = AddObserver(TimeEvent(0), eventReceptorCommand);
m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
}
// use a templated method to get the right offset when casting to void*
template <typename T>
void Disconnect(T* receiver)
{
auto i = m_ReceiverToObserverTagsMap.find(static_cast<void*>(receiver));
if (i == m_ReceiverToObserverTagsMap.end())
return;
const std::list<unsigned long>& tags = i->second;
for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter)
{
RemoveObserver(*tagIter);
}
m_ReceiverToObserverTagsMap.erase(i);
}
/**
* \brief Convenience method that returns the time step currently selected by the controller.
*/
TimeStepType GetSelectedTimeStep() const;
/**
* \brief Convenience method that returns the time point that corresponds to the selected
* time step. The conversion is done using the time geometry of the controller.
* If the time geometry is not yet set, this function will always return 0.0.
*/
TimePointType GetSelectedTimePoint() const;
protected:
TimeNavigationController();
~TimeNavigationController() override;
TimeGeometry::ConstPointer m_InputWorldTimeGeometry;
bool m_BlockUpdate;
typedef std::map<void*, std::list<unsigned long>> ObserverTagsMapType;
ObserverTagsMapType m_ReceiverToObserverTagsMap;
};
} // namespace mitk
#endif
diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
index be8a030d6a..c269481332 100644
--- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
+++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
@@ -1,548 +1,548 @@
/*============================================================================
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 <algorithm>
#include <nlohmann/json.hpp>
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;
upperBound = std::clamp(upperBound, -1e300, 1e300);
lowerBound = std::clamp(lowerBound, -1e300, 1e300);
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
extremes 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::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 *>(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::IOPixelEnum::SCALAR &&
image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8)
{
// the windows compiler complains about ambiguous 'pow' call, therefore static casting to (double, int)
if (minValue == -(pow((double)2.0, static_cast<int>(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<ScalarType>(numPixelsInDataset);
const auto maxCountFraction = maxCount / static_cast<ScalarType>(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
+ // now we can assume more than three distinct 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;
}
}
namespace mitk
{
void to_json(nlohmann::json& j, const LevelWindow& lw)
{
j = nlohmann::json{
{"Fixed", lw.IsFixed()},
{"IsFloatingImage", lw.IsFloatingValues()},
{"CurrentSettings", {
{"Level", lw.GetLevel()},
{"Window", lw.GetWindow()}}},
{"DefaultSettings", {
{"Level", lw.GetDefaultLevel()},
{"Window", lw.GetDefaultWindow()}}},
{"CurrentRange", {
{"Min", lw.GetRangeMin()},
{"Max", lw.GetRangeMax()}}}};
}
void from_json(const nlohmann::json& j, LevelWindow& lw)
{
lw.SetRangeMinMax(
j["CurrentRange"]["Min"].get<ScalarType>(),
j["CurrentRange"]["Max"].get<ScalarType>());
lw.SetDefaultLevelWindow(
j["DefaultSettings"]["Level"].get<ScalarType>(),
j["DefaultSettings"]["Window"].get<ScalarType>());
lw.SetLevelWindow(
j["CurrentSettings"]["Level"].get<ScalarType>(),
j["CurrentSettings"]["Window"].get<ScalarType>());
lw.SetFixed(j["Fixed"].get<bool>());
lw.SetFloatingValues(j["IsFloatingImage"].get<bool>());
}
}
diff --git a/Modules/Core/src/Interactions/mitkStateMachineState.cpp b/Modules/Core/src/Interactions/mitkStateMachineState.cpp
index a3aa4ddfdb..81b60052a3 100755
--- a/Modules/Core/src/Interactions/mitkStateMachineState.cpp
+++ b/Modules/Core/src/Interactions/mitkStateMachineState.cpp
@@ -1,105 +1,105 @@
/*============================================================================
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 "mitkStateMachineState.h"
mitk::StateMachineState::StateMachineState(const std::string &stateName, const std::string &stateMode)
: m_Name(stateName), m_StateMode(stateMode)
{
}
std::string mitk::StateMachineState::GetMode() const
{
return m_StateMode;
}
mitk::StateMachineState::~StateMachineState()
{
m_Transitions.clear();
}
bool mitk::StateMachineState::AddTransition(StateMachineTransition::Pointer transition)
{
for (auto it = m_Transitions.begin(); it != m_Transitions.end(); ++it)
{
if (transition.GetPointer() == (*it).GetPointer())
return false;
}
m_Transitions.push_back(transition);
return true;
}
mitk::StateMachineTransition::Pointer mitk::StateMachineState::GetTransition(const std::string &eventClass,
const std::string &eventVariant)
{
TransitionVector transitions = this->GetTransitionList(eventClass, eventVariant);
if (transitions.size() > 1)
{
MITK_WARN << "Multiple transitions have been found for event. Use non-deprecated method "
"StateMachineState::GetTransitionList() instead!";
}
if (transitions.empty())
{
return nullptr;
}
else
{
return transitions.at(0);
}
}
mitk::StateMachineState::TransitionVector mitk::StateMachineState::GetTransitionList(const std::string &eventClass,
const std::string &eventVariant)
{
TransitionVector transitions;
mitk::StateMachineTransition::Pointer t = mitk::StateMachineTransition::New("", eventClass, eventVariant);
for (auto it = m_Transitions.begin(); it != m_Transitions.end(); ++it)
{
if (**it == *t) // do not switch it and t, order matters, see mitk::StateMachineTransition == operator
transitions.push_back(*it);
}
return transitions;
}
std::string mitk::StateMachineState::GetName() const
{
return m_Name;
}
//##Documentation
-//## Post-processing step, when builing StateMachine from XML.
+//## Post-processing step, when building StateMachine from XML.
//## Parse all transitions and find the State that matches the String-Name.
bool mitk::StateMachineState::ConnectTransitions(StateMap *allStates)
{
for (auto transIt = m_Transitions.begin(); transIt != m_Transitions.end(); ++transIt)
{
bool found = false;
for (auto stateIt = allStates->begin(); stateIt != allStates->end(); ++stateIt)
{
if ((*stateIt)->GetName() == (*transIt)->GetNextStateName())
{
(*transIt)->SetNextState(*stateIt);
found = true;
break;
}
}
if (!found)
{
MITK_WARN << "Target State not found in StateMachine.";
return false; // only reached if no state matching the string is found
}
}
return true;
}
diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp
index 3d6da2125f..e565ad34ab 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 <mitkAbstractTransformGeometry.h>
#include <mitkDataNode.h>
#include <mitkImageSliceSelector.h>
#include <mitkLevelWindowProperty.h>
#include <mitkLookupTableProperty.h>
#include <mitkPixelType.h>
#include <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkPropertyNameHelper.h>
#include <mitkResliceMethodProperty.h>
#include <mitkVtkResliceInterpolationProperty.h>
//#include <mitkTransferFunction.h>
#include "mitkImageStatisticsHolder.h"
#include "mitkPlaneClipping.h"
#include <mitkTransferFunctionProperty.h>
// MITK Rendering
#include "mitkImageVtkMapper2D.h"
#include "vtkMitkLevelWindowFilter.h"
#include "vtkMitkThickSlicesFilter.h"
#include "vtkNeverTranslucentTexture.h"
// VTK
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkColorTransferFunction.h>
#include <vtkGeneralTransform.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkMatrix4x4.h>
#include <vtkPlaneSource.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkTransform.h>
// ITK
#include <itkRGBAPixel.h>
#include <mitkRenderingModeProperty.h>
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<decltype(numTimeSteps)> t = 0; t < numTimeSteps; ++t)
{
const auto numChannels = image->GetNumberOfChannels();
for (std::remove_const_t<decltype(numChannels)> 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 sagittal) 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<const mitk::Image *>(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<mitk::Image *>(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 conversion
// 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<const PlaneGeometry *>(worldGeometry);
if (thickSlicesMode > 0)
{
double dataZSpacing = 1.0;
Vector3D normInIndex, normal;
const auto *abstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(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 before. 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
+ // Bounds information for reslicing (only required 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<int>(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[1] = static_cast<int>(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[2] = static_cast<int>(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5);
textureClippingBounds[3] = static_cast<int>(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
auto componentType = image->GetPixelType().GetComponentType();
switch (componentType)
{
case itk::IOComponentEnum::UCHAR:
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData<unsigned char>(renderer);
break;
case itk::IOComponentEnum::USHORT:
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData<unsigned short>(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<mitk::ColorProperty *>(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<mitk::ColorProperty *>(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<mitk::ColorProperty *>(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<mitk::RenderingModeProperty *>(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<mitk::LookupTableProperty *>(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 iteration of the rendering.
}
localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable);
}
void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer)
{
mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty *>(
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<mitk::Image *>(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<mitk::Image *>(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::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<mitk::Image *>(node->GetData()), false, true); // fallback
}
contrast.SetLevelWindow(level, window, true);
}
else
{
contrast.SetAuto(static_cast<mitk::Image *>(node->GetData()), false, true); // fallback
}
node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer);
}
if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) &&
(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 <typename TPixel>
vtkSmartPointer<vtkPolyData> 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<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points
// We take the pointer to the first pixel of the image
auto* currentPixel = static_cast<TPixel*>(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<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::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 sagittal
vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_Reslicer->GetResliceAxes();
trans->SetMatrix(matrix);
// transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal)
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<vtkImageExtractComponents>::New())
{
m_LevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
// Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
m_Texture = vtkSmartPointer<vtkNeverTranslucentTexture>::New().GetPointer();
m_DefaultLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_BinaryLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_ColorLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_ImageActor = vtkSmartPointer<vtkActor>::New();
m_ShadowOutlineActor = vtkSmartPointer<vtkActor>::New();
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_EmptyActors = vtkSmartPointer<vtkPropAssembly>::New();
m_Reslicer = mitk::ExtractSliceFilter::New();
m_TSFilter = vtkSmartPointer<vtkMitkThickSlicesFilter>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_ReslicedImage = vtkSmartPointer<vtkImageData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::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/Rendering/mitkPointSetVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp
index 55d87b3364..9f8a5db819 100644
--- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp
+++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp
@@ -1,781 +1,781 @@
/*============================================================================
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 "mitkPointSetVtkMapper2D.h"
// mitk includes
#include "mitkVtkPropRenderer.h"
#include <mitkDataNode.h>
#include <mitkPlaneGeometry.h>
#include <mitkPointSet.h>
#include <mitkProperties.h>
// vtk includes
#include <vtkActor.h>
#include <vtkCellArray.h>
#include <vtkFloatArray.h>
#include <vtkGlyph3D.h>
#include <vtkGlyphSource2D.h>
#include <vtkLine.h>
#include <vtkPointData.h>
#include <vtkPolyDataMapper.h>
#include <vtkPropAssembly.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>
#include <cstdlib>
namespace
{
double GetScreenResolution(const mitk::BaseRenderer* renderer)
{
if (nullptr == renderer)
return 1.0;
mitk::Point2D pD1, pD2;
pD1[0] = 0.0;
pD1[1] = 0.0;
pD2[0] = 0.0;
pD2[1] = 1.0;
// Calculate world coordinates of in-plane screen pixels (0, 0) and (0, 1).
mitk::Point3D pW1, pW2;
renderer->DisplayToWorld(pD1, pW1);
renderer->DisplayToWorld(pD2, pW2);
// For 2D renderers, the distance between these points is the screen resolution.
return pW1.EuclideanDistanceTo(pW2);
}
}
// constructor LocalStorage
mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage()
{
// points
m_UnselectedPoints = vtkSmartPointer<vtkPoints>::New();
m_SelectedPoints = vtkSmartPointer<vtkPoints>::New();
m_ContourPoints = vtkSmartPointer<vtkPoints>::New();
// scales
m_UnselectedScales = vtkSmartPointer<vtkFloatArray>::New();
m_SelectedScales = vtkSmartPointer<vtkFloatArray>::New();
// distances
m_DistancesBetweenPoints = vtkSmartPointer<vtkFloatArray>::New();
// lines
m_ContourLines = vtkSmartPointer<vtkCellArray>::New();
// glyph source (provides the different shapes)
m_UnselectedGlyphSource2D = vtkSmartPointer<vtkGlyphSource2D>::New();
m_SelectedGlyphSource2D = vtkSmartPointer<vtkGlyphSource2D>::New();
// glyphs
m_UnselectedGlyph3D = vtkSmartPointer<vtkGlyph3D>::New();
m_SelectedGlyph3D = vtkSmartPointer<vtkGlyph3D>::New();
// polydata
m_VtkUnselectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
m_VtkSelectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
m_VtkContourPolyData = vtkSmartPointer<vtkPolyData>::New();
// actors
m_UnselectedActor = vtkSmartPointer<vtkActor>::New();
m_SelectedActor = vtkSmartPointer<vtkActor>::New();
m_ContourActor = vtkSmartPointer<vtkActor>::New();
// mappers
m_VtkUnselectedPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_VtkSelectedPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_VtkContourPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
// propassembly
m_PropAssembly = vtkSmartPointer<vtkPropAssembly>::New();
}
// destructor LocalStorage
mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage()
{
}
// input for this mapper ( = point set)
const mitk::PointSet *mitk::PointSetVtkMapper2D::GetInput() const
{
return static_cast<const mitk::PointSet *>(GetDataNode()->GetData());
}
// constructor PointSetVtkMapper2D
mitk::PointSetVtkMapper2D::PointSetVtkMapper2D()
: m_ShowContour(false),
m_CloseContour(false),
m_ShowPoints(true),
m_ShowDistances(false),
m_DistancesDecimalDigits(1),
m_ShowAngles(false),
m_ShowDistantLines(false),
m_LineWidth(1),
m_PointLineWidth(1),
m_Point2DSize(6),
m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS),
m_FillShape(false),
m_DistanceToPlane(4.0f),
m_FixedSizeOnScreen(false)
{
}
// destructor
mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D()
{
}
// reset mapper so that nothing is displayed e.g. toggle visibility of the propassembly
void mitk::PointSetVtkMapper2D::ResetMapper(BaseRenderer *renderer)
{
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
ls->m_PropAssembly->VisibilityOff();
}
// returns propassembly
vtkProp *mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
return ls->m_PropAssembly;
}
static bool makePerpendicularVector2D(const mitk::Vector2D &in, mitk::Vector2D &out)
{
// The dot product of orthogonal vectors is zero.
// In two dimensions the slopes of perpendicular lines are negative reciprocals.
if ((fabs(in[0]) > 0) && ((fabs(in[0]) > fabs(in[1])) || (in[1] == 0)))
{
// negative reciprocal
out[0] = -in[1] / in[0];
out[1] = 1;
out.Normalize();
return true;
}
else if (fabs(in[1]) > 0)
{
out[0] = 1;
// negative reciprocal
out[1] = -in[0] / in[1];
out.Normalize();
return true;
}
else
return false;
}
void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer *renderer)
{
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
unsigned i = 0;
// The vtk text actors need to be removed manually from the propassembly
// since the same vtk text actors are not overwritten within this function,
// but new actors are added to the propassembly each time this function is executed.
// Thus, the actors from the last call must be removed in the beginning.
for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++)
{
if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i)))
ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i));
}
for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++)
{
if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i)))
ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i));
}
for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++)
{
if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i)))
ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i));
}
// initialize polydata here, otherwise we have update problems when
// executing this function again
ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
ls->m_VtkSelectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
ls->m_VtkContourPolyData = vtkSmartPointer<vtkPolyData>::New();
// get input point set and update the PointSet
mitk::PointSet::Pointer input = const_cast<mitk::PointSet *>(this->GetInput());
// only update the input data, if the property tells us to
bool update = true;
this->GetDataNode()->GetBoolProperty("updateDataOnRender", update);
if (update == true)
input->Update();
int timestep = this->GetTimestep();
mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep);
if (itkPointSet.GetPointer() == nullptr)
{
ls->m_PropAssembly->VisibilityOff();
return;
}
// iterator for point set
mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin();
// PointDataContainer has additional information to each point, e.g. whether
// it is selected or not
mitk::PointSet::PointDataContainer::Iterator pointDataIter;
pointDataIter = itkPointSet->GetPointData()->Begin();
// check if the list for the PointDataContainer is the same size as the PointsContainer.
// If not, then the points were inserted manually and can not be visualized according to the PointData
// (selected/unselected)
bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size());
if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken)
{
ls->m_PropAssembly->VisibilityOff();
return;
}
ls->m_PropAssembly->VisibilityOn();
// empty point sets, cellarrays, scalars
ls->m_UnselectedPoints->Reset();
ls->m_SelectedPoints->Reset();
ls->m_ContourPoints->Reset();
ls->m_ContourLines->Reset();
ls->m_UnselectedScales->Reset();
ls->m_SelectedScales->Reset();
ls->m_DistancesBetweenPoints->Reset();
ls->m_VtkTextLabelActors.clear();
ls->m_VtkTextDistanceActors.clear();
ls->m_VtkTextAngleActors.clear();
ls->m_UnselectedScales->SetNumberOfComponents(3);
ls->m_SelectedScales->SetNumberOfComponents(3);
int NumberContourPoints = 0;
bool pointsOnSameSideOfPlane = false;
const int text2dDistance = 10;
// initialize points with a random start value
// current point in point set
itk::Point<ScalarType> point = pointsIter->Value();
mitk::Point3D p = point; // currently visited point
mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point")
mitk::Vector3D vec; // p - lastP
mitk::Vector3D lastVec; // lastP - point before lastP
vec.Fill(0.0);
lastVec.Fill(0.0);
mitk::Point2D pt2d;
pt2d[0] = point[0]; // projected_p in display coordinates
pt2d[1] = point[1];
mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d")
mitk::Point2D preLastPt2d = pt2d; // projected_p in display coordinates before lastPt2
const mitk::PlaneGeometry *geo2D = renderer->GetCurrentWorldPlaneGeometry();
double resolution = GetScreenResolution(renderer);
vtkLinearTransform *dataNodeTransform = input->GetGeometry()->GetVtkTransform();
int count = 0;
for (pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++)
{
lastP = p; // valid for number of points count > 0
preLastPt2d = lastPt2d; // valid only for count > 1
lastPt2d = pt2d; // valid for number of points count > 0
lastVec = vec; // valid only for counter > 1
// get current point in point set
point = pointsIter->Value();
// transform point
{
float vtkp[3];
itk2vtk(point, vtkp);
dataNodeTransform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, point);
}
p[0] = point[0];
p[1] = point[1];
p[2] = point[2];
renderer->WorldToDisplay(p, pt2d);
vec = p - lastP; // valid only for counter > 0
// compute distance to current plane
float dist = geo2D->Distance(point);
// measure distance in screen pixel units if requested
if (m_FixedSizeOnScreen)
{
dist /= resolution;
}
// draw markers on slices a certain distance away from the points
// location according to the tolerance threshold (m_DistanceToPlane)
if (dist < m_DistanceToPlane)
{
// is point selected or not?
if (pointDataIter->Value().selected)
{
ls->m_SelectedPoints->InsertNextPoint(point[0], point[1], point[2]);
// point is scaled according to its distance to the plane
ls->m_SelectedScales->InsertNextTuple3(
std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0);
}
else
{
ls->m_UnselectedPoints->InsertNextPoint(point[0], point[1], point[2]);
// point is scaled according to its distance to the plane
ls->m_UnselectedScales->InsertNextTuple3(
std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0);
}
//---- LABEL -----//
// paint label for each point if available
if (dynamic_cast<mitk::StringProperty *>(this->GetDataNode()->GetProperty("label")) != nullptr)
{
const char *pointLabel =
dynamic_cast<mitk::StringProperty *>(this->GetDataNode()->GetProperty("label"))->GetValue();
std::string l = pointLabel;
if (input->GetSize() > 1)
{
std::stringstream ss;
ss << pointsIter->Index();
l.append(ss.str());
}
ls->m_VtkTextActor = vtkSmartPointer<vtkTextActor>::New();
ls->m_VtkTextActor->SetDisplayPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance);
ls->m_VtkTextActor->SetInput(l.c_str());
ls->m_VtkTextActor->GetTextProperty()->SetOpacity(100);
float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0};
// check if there is a color property
GetDataNode()->GetColor(unselectedColor);
ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]);
ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor);
}
}
// draw contour, distance text and angle text in render window
// lines between points, which intersect the current plane, are drawn
if (m_ShowContour && count > 0)
{
ScalarType distance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(point);
ScalarType lastDistance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(lastP);
pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5;
// Points must be on different side of plane in order to draw a contour.
// If "show distant lines" is enabled this condition is disregarded.
if (!pointsOnSameSideOfPlane || m_ShowDistantLines)
{
vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New();
ls->m_ContourPoints->InsertNextPoint(lastP[0], lastP[1], lastP[2]);
line->GetPointIds()->SetId(0, NumberContourPoints);
NumberContourPoints++;
ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]);
line->GetPointIds()->SetId(1, NumberContourPoints);
NumberContourPoints++;
ls->m_ContourLines->InsertNextCell(line);
if (m_ShowDistances) // calculate and print distance between adjacent points
{
float distancePoints = point.EuclideanDistanceTo(lastP);
std::stringstream buffer;
buffer << std::fixed << std::setprecision(m_DistancesDecimalDigits) << distancePoints << " mm";
// compute desired display position of text
Vector2D vec2d = pt2d - lastPt2d;
makePerpendicularVector2D(vec2d,
vec2d); // text is rendered within text2dDistance perpendicular to current line
Vector2D pos2d = (lastPt2d.GetVectorFromOrigin() + pt2d.GetVectorFromOrigin()) * 0.5 + vec2d * text2dDistance;
ls->m_VtkTextActor = vtkSmartPointer<vtkTextActor>::New();
ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]);
ls->m_VtkTextActor->SetInput(buffer.str().c_str());
ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0);
ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor);
}
if (m_ShowAngles && count > 1) // calculate and print angle between connected lines
{
std::stringstream buffer;
buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector()) * 180 / vnl_math::pi << "°";
// compute desired display position of text
Vector2D vec2d = pt2d - lastPt2d; // first arm enclosing the angle
vec2d.Normalize();
Vector2D lastVec2d = lastPt2d - preLastPt2d; // second arm enclosing the angle
lastVec2d.Normalize();
vec2d = vec2d - lastVec2d; // vector connecting both arms
vec2d.Normalize();
// middle between two vectors that enclose the angle
Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance;
ls->m_VtkTextActor = vtkSmartPointer<vtkTextActor>::New();
ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]);
ls->m_VtkTextActor->SetInput(buffer.str().c_str());
ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0);
ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor);
}
}
}
if (pointDataIter != itkPointSet->GetPointData()->End())
{
pointDataIter++;
count++;
}
}
// add each single text actor to the assembly
for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++)
{
ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i));
}
for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++)
{
ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i));
}
for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++)
{
ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i));
}
//---- CONTOUR -----//
// create lines between the points which intersect the plane
if (m_ShowContour)
{
// draw line between first and last point which is rendered
if (m_CloseContour && NumberContourPoints > 1)
{
vtkSmartPointer<vtkLine> closingLine = vtkSmartPointer<vtkLine>::New();
closingLine->GetPointIds()->SetId(0, 0); // index of first point
closingLine->GetPointIds()->SetId(1, NumberContourPoints - 1); // index of last point
ls->m_ContourLines->InsertNextCell(closingLine);
}
ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints);
ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines);
ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData);
ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper);
ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth);
ls->m_PropAssembly->AddPart(ls->m_ContourActor);
}
// the point set must be transformed in order to obtain the appropriate glyph orientation
// according to the current view
vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> a, b = vtkSmartPointer<vtkMatrix4x4>::New();
a = geo2D->GetVtkTransform()->GetMatrix();
b->DeepCopy(a);
// delete transformation from matrix, only take orientation
b->SetElement(3, 3, 1);
b->SetElement(2, 3, 0);
b->SetElement(1, 3, 0);
b->SetElement(0, 3, 0);
b->SetElement(3, 2, 0);
b->SetElement(3, 1, 0);
b->SetElement(3, 0, 0);
Vector3D spacing = geo2D->GetSpacing();
- // If you find a way to simplyfy the following, feel free to change!
+ // If you find a way to simplify the following, feel free to change!
b->SetElement(0, 0, b->GetElement(0, 0) / spacing[0]);
b->SetElement(1, 0, b->GetElement(1, 0) / spacing[0]);
b->SetElement(2, 0, b->GetElement(2, 0) / spacing[0]);
b->SetElement(1, 1, b->GetElement(1, 1) / spacing[1]);
b->SetElement(2, 1, b->GetElement(2, 1) / spacing[1]);
b->SetElement(0, 2, b->GetElement(0, 2) / spacing[2]);
b->SetElement(1, 2, b->GetElement(1, 2) / spacing[2]);
b->SetElement(2, 2, b->GetElement(2, 2) / spacing[2]);
transform->SetMatrix(b);
//---- UNSELECTED POINTS -----//
// apply properties to glyph
ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty);
if (m_FillShape)
ls->m_UnselectedGlyphSource2D->FilledOn();
else
ls->m_UnselectedGlyphSource2D->FilledOff();
// apply transform
vtkSmartPointer<vtkTransformFilter> transformFilterU = vtkSmartPointer<vtkTransformFilter>::New();
transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort());
transformFilterU->SetTransform(transform);
ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints);
ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales);
// apply transform of current plane to glyphs
ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort());
ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData);
ls->m_UnselectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0);
ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector();
ls->m_UnselectedGlyph3D->SetVectorModeToUseVector();
ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort());
ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper);
ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth);
ls->m_PropAssembly->AddPart(ls->m_UnselectedActor);
//---- SELECTED POINTS -----//
ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond();
ls->m_SelectedGlyphSource2D->CrossOn();
ls->m_SelectedGlyphSource2D->FilledOff();
// apply transform
vtkSmartPointer<vtkTransformFilter> transformFilterS = vtkSmartPointer<vtkTransformFilter>::New();
transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort());
transformFilterS->SetTransform(transform);
ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints);
ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales);
// apply transform of current plane to glyphs
ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort());
ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData);
ls->m_SelectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0);
ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector();
ls->m_SelectedGlyph3D->SetVectorModeToUseVector();
ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort());
ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper);
ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth);
ls->m_PropAssembly->AddPart(ls->m_SelectedActor);
}
void mitk::PointSetVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
const mitk::DataNode *node = GetDataNode();
if (node == nullptr)
return;
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
// check whether the input data has been changed
bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode());
// toggle visibility
bool visible = true;
node->GetVisibility(visible, renderer, "visible");
if (!visible)
{
ls->m_UnselectedActor->VisibilityOff();
ls->m_SelectedActor->VisibilityOff();
ls->m_ContourActor->VisibilityOff();
ls->m_PropAssembly->VisibilityOff();
return;
}
else
{
ls->m_PropAssembly->VisibilityOn();
}
node->GetBoolProperty("show contour", m_ShowContour, renderer);
node->GetBoolProperty("close contour", m_CloseContour, renderer);
node->GetBoolProperty("show points", m_ShowPoints, renderer);
node->GetBoolProperty("show distances", m_ShowDistances, renderer);
node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer);
node->GetBoolProperty("show angles", m_ShowAngles, renderer);
node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer);
node->GetIntProperty("line width", m_LineWidth, renderer);
node->GetIntProperty("point line width", m_PointLineWidth, renderer);
if (!node->GetFloatProperty(
"point 2D size", m_Point2DSize, renderer)) // re-defined to float 2015-08-13, keep a fallback
{
int oldPointSize = m_Point2DSize;
if (node->GetIntProperty("point 2D size", oldPointSize, renderer))
{
m_Point2DSize = oldPointSize;
}
}
node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer);
node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer);
node->GetBoolProperty("Pointset.2D.fixed size on screen", m_FixedSizeOnScreen, renderer);
mitk::PointSetShapeProperty::Pointer shape =
dynamic_cast<mitk::PointSetShapeProperty *>(this->GetDataNode()->GetProperty("Pointset.2D.shape", renderer));
if (shape.IsNotNull())
{
m_IDShapeProperty = shape->GetPointSetShape();
}
// check for color props and use it for rendering of selected/unselected points and contour
// due to different params in VTK (double/float) we have to convert
float opacity = 1.0;
GetDataNode()->GetOpacity(opacity, renderer);
// apply color and opacity
if (m_ShowPoints)
{
float unselectedColor[4];
double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red
ls->m_UnselectedActor->VisibilityOn();
ls->m_SelectedActor->VisibilityOn();
// check if there is a color property
GetDataNode()->GetColor(unselectedColor);
// get selected color property
if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr)
{
mitk::Color tmpColor = dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))
->GetValue();
selectedColor[0] = tmpColor[0];
selectedColor[1] = tmpColor[1];
selectedColor[2] = tmpColor[2];
selectedColor[3] = 1.0f; // alpha value
}
else if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr)
{
mitk::Color tmpColor =
dynamic_cast<mitk::ColorProperty *>(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor"))
->GetValue();
selectedColor[0] = tmpColor[0];
selectedColor[1] = tmpColor[1];
selectedColor[2] = tmpColor[2];
selectedColor[3] = 1.0f; // alpha value
}
ls->m_SelectedActor->GetProperty()->SetColor(selectedColor);
ls->m_SelectedActor->GetProperty()->SetOpacity(opacity);
ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]);
ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity);
}
else
{
ls->m_UnselectedActor->VisibilityOff();
ls->m_SelectedActor->VisibilityOff();
}
if (m_ShowContour)
{
double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red
ls->m_ContourActor->VisibilityOn();
// get contour color property
if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr)
{
mitk::Color tmpColor =
dynamic_cast<mitk::ColorProperty *>(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))
->GetValue();
contourColor[0] = tmpColor[0];
contourColor[1] = tmpColor[1];
contourColor[2] = tmpColor[2];
contourColor[3] = 1.0f;
}
else if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) != nullptr)
{
mitk::Color tmpColor =
dynamic_cast<mitk::ColorProperty *>(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor"))
->GetValue();
contourColor[0] = tmpColor[0];
contourColor[1] = tmpColor[1];
contourColor[2] = tmpColor[2];
contourColor[3] = 1.0f;
}
ls->m_ContourActor->GetProperty()->SetColor(contourColor);
ls->m_ContourActor->GetProperty()->SetOpacity(opacity);
}
else
{
ls->m_ContourActor->VisibilityOff();
}
if (needGenerateData)
{
// create new vtk render objects (e.g. a circle for a point)
this->CreateVTKRenderObjects(renderer);
}
}
void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite)
{
node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite);
node->AddProperty("point line width", mitk::IntProperty::New(1), renderer, overwrite);
node->AddProperty("point 2D size", mitk::FloatProperty::New(6), renderer, overwrite);
node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty("show distances", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite);
node->AddProperty("show angles", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("show distant lines", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("layer", mitk::IntProperty::New(1), renderer, overwrite);
node->AddProperty("Pointset.2D.fill shape",
mitk::BoolProperty::New(false),
renderer,
overwrite); // fill or do not fill the glyph shape
mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New();
node->AddProperty("Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite);
node->AddProperty("Pointset.2D.distance to plane",
mitk::FloatProperty::New(4.0f),
renderer,
overwrite); // show the point at a certain distance above/below the 2D imaging plane.
node->AddProperty("Pointset.2D.fixed size on screen", mitk::BoolProperty::New(false), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
diff --git a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp
index 65f234967d..ae7dd51fa9 100644
--- a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp
+++ b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp
@@ -1,795 +1,795 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include <iostream>
#include "mitkClippedSurfaceBoundsCalculator.h"
#include "mitkGeometry3D.h"
#include "mitkNumericTypes.h"
#include "mitkPlaneGeometry.h"
static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are inside the bounding box
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
// Check planes which are only on one slice:
// Slice 0
mitk::Point3D origin;
origin[0] = 511;
origin[1] = 0;
origin[2] = 0;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0, 0, 1);
mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New();
planeOnSliceZero->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceZero, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0");
// Slice 3
origin[2] = 3;
mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New();
planeOnSliceThree->InitializePlane(origin, normal);
planeOnSliceThree->SetImageGeometry(false);
calculator->SetInput(planeOnSliceThree, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3");
// Slice 17
origin[2] = 17;
mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New();
planeOnSliceSeventeen->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceSeventeen, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17");
// Slice 20
origin[2] = 19;
mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New();
planeOnSliceTwenty->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceTwenty, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19");
delete calculator;
}
static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are inside the bounding box
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
// Check planes which are only on one slice:
// Slice 0
mitk::Point3D origin;
origin[0] = 510; // Set to 511.9 so that the intersection point is inside the bounding box
origin[1] = 0;
origin[2] = 0;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 1, 0, 0);
mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New();
planeSagittalOne->InitializePlane(origin, normal);
calculator->SetInput(planeSagittalOne, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
// Slice 3
origin[0] = 256;
MITK_INFO << "Case1 origin: " << origin;
mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New();
planeSagittalTwo->InitializePlane(origin, normal);
MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal();
MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin();
calculator->SetInput(planeSagittalTwo, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
// Slice 17
origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box
mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New();
planeOnSliceSeventeen->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceSeventeen, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
// Crooked planes:
origin[0] = 0;
origin[1] = 507;
origin[2] = 0;
normal[0] = 1;
normal[1] = -1;
normal[2] = 1;
mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New();
planeCrookedOne->InitializePlane(origin, normal);
calculator->SetInput(planeCrookedOne, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 5,
"Check if plane is from slice 0 to slice 5 with inclined plane");
origin[0] = 512;
origin[1] = 0;
origin[2] = 16;
mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New();
planeCrookedTwo->InitializePlane(origin, normal);
calculator->SetInput(planeCrookedTwo, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 16 && minMax.second == 19,
"Check if plane is from slice 16 to slice 19 with inclined plane");
origin[0] = 510;
origin[1] = 0;
origin[2] = 0;
normal[1] = 0;
normal[2] = 0.04;
mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New();
planeCrookedThree->InitializePlane(origin, normal);
calculator->SetInput(planeCrookedThree, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19,
"Check if plane is from slice 0 to slice 19 with inclined plane");
delete calculator;
}
static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are outside of the bounding box
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
// In front of the bounding box
mitk::Point3D origin;
origin[0] = 510;
origin[1] = 0;
origin[2] = -5;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0, 0, 1);
mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New();
planeInFront->InitializePlane(origin, normal);
calculator->SetInput(planeInFront, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Behind the bounding box
origin[2] = 515;
mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New();
planeBehind->InitializePlane(origin, normal);
calculator->SetInput(planeBehind, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Above
origin[1] = 515;
mitk::FillVector3D(normal, 0, 1, 0);
mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New();
planeAbove->InitializePlane(origin, normal);
calculator->SetInput(planeAbove, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Below
origin[1] = -5;
mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New();
planeBelow->InitializePlane(origin, normal);
calculator->SetInput(planeBelow, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Left side
origin[0] = -5;
mitk::FillVector3D(normal, 1, 0, 0);
mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New();
planeLeftSide->InitializePlane(origin, normal);
calculator->SetInput(planeLeftSide, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Right side
origin[1] = 515;
mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New();
planeRightSide->InitializePlane(origin, normal);
calculator->SetInput(planeRightSide, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
delete calculator;
}
static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D,
mitk::BaseGeometry::Pointer secondGeometry3D)
{
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer firstImage = mitk::Image::New();
firstImage->Initialize(mitk::MakePixelType<int, int, 1>(), *(firstGeometry3D.GetPointer()));
calculator->SetInput(secondGeometry3D, firstImage);
calculator->Update();
auto minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
delete calculator;
}
static void CheckIntersectionWithPointCloud(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are inside the bounding box
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
{
mitk::Point3D pnt1, pnt2;
pnt1[0] = 3;
pnt1[1] = 5;
pnt1[2] = 3;
pnt2[0] = 8;
pnt2[1] = 3;
pnt2[2] = 8;
mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist;
pointlist.push_back(pnt1);
pointlist.push_back(pnt2);
mitk::ClippedSurfaceBoundsCalculator calculator;
calculator.SetInput(pointlist, image);
calculator.Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8,
"Check if points span from slice 3 to slice 8 in axial");
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX();
MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5,
"Check if points span from slice 3 to slice 5 in sagittal");
}
{
mitk::Point3D pnt1, pnt2;
pnt1.Fill(-3);
pnt2.Fill(600);
mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist;
pointlist.push_back(pnt1);
pointlist.push_back(pnt2);
mitk::ClippedSurfaceBoundsCalculator calculator;
calculator.SetInput(pointlist, image);
calculator.Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19,
"Check if points are correctly clipped to slice 0 and slice 19 in axial");
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX();
MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511,
"Check if points are correctly clipped to slice 0 and slice 511 in sagittal");
}
}
static void CheckIntersectionWithRotatedGeometry()
{
// Define origin for second Geometry3D;
mitk::Point3D origin3;
origin3[0] = -45.25;
origin3[1] = -113.22;
origin3[2] = 0.62;
// Define normal:
mitk::Vector3D normal3;
normal3[0] = -0.02;
normal3[1] = -0.18;
normal3[2] = 2.99;
mitk::Vector3D spacing;
spacing[0] = 1.43;
spacing[1] = 1.43;
spacing[2] = 3.0;
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New();
planeGeometry3->InitializePlane(origin3, normal3);
planeGeometry3->SetSpacing(spacing);
mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds();
moreBounds[0] = 0;
moreBounds[1] = 64;
moreBounds[2] = 0;
moreBounds[3] = 140;
moreBounds[4] = 0;
moreBounds[5] = 1;
planeGeometry3->SetBounds(moreBounds);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New();
rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry3.GetPointer()), 25);
rotatedSlicedGeometry3D->SetImageGeometry(true);
mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform();
mitk::AffineTransform3D::MatrixType matrix;
matrix[0][0] = 1.43;
matrix[0][1] = -0.01;
matrix[0][2] = -0.02;
matrix[0][0] = 0.01;
matrix[1][1] = 1.43;
matrix[2][2] = -0.18;
matrix[0][0] = 0.01;
matrix[1][1] = 0.08;
matrix[2][2] = 2.99;
geom->SetMatrix(matrix);
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(rotatedSlicedGeometry3D.GetPointer()));
// Check planes which are only on one slice:
{
// last Slice
mitk::Point3D origin;
origin[0] = -47.79;
origin[1] = 81.41;
origin[2] = 84.34;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0.02, 0.18, -2.99);
mitk::Vector3D spacing;
spacing[0] = 1.43;
spacing[1] = 1.43;
spacing[2] = 3.0;
mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New();
planeOnSliceZero->InitializePlane(origin, normal);
planeOnSliceZero->SetSpacing(spacing);
calculator->SetInput(planeOnSliceZero, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 24 && minMax.second == 24, "Check if plane is on slice 24");
}
{
// Slice 0-24
mitk::Point3D origin;
origin[0] = -45;
origin[1] = -105;
origin[2] = 35;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 1.43, 0.01, 0.01);
mitk::Vector3D spacing;
spacing[0] = 1.43;
spacing[1] = 3.0;
spacing[2] = 1.43;
mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New();
planeOnSliceZero->InitializePlane(origin, normal);
planeOnSliceZero->SetSpacing(spacing);
calculator->SetInput(planeOnSliceZero, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first != minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 24, "Check if plane is on slices 0-24");
}
delete calculator;
}
static void CheckIntersectionWithRotatedGeometry90()
{
// Define origin for second Geometry3D;
mitk::Point3D origin3;
origin3[0] = 0.0;
origin3[1] = 0.0;
origin3[2] = 0.0;
// Define normal:
mitk::Vector3D normal3;
normal3[0] = 0;
normal3[1] = 0;
normal3[2] = 1;
mitk::Vector3D spacing;
spacing[0] = 1.0;
spacing[1] = 2.0;
spacing[2] = 1.0;
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New();
planeGeometry3->InitializePlane(origin3, normal3);
planeGeometry3->SetSpacing(spacing);
mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds();
moreBounds[0] = 0;
moreBounds[1] = 50;
moreBounds[2] = 0;
moreBounds[3] = 50;
moreBounds[4] = 0;
moreBounds[5] = 1;
planeGeometry3->SetBounds(moreBounds);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New();
rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry3.GetPointer()), 50);
rotatedSlicedGeometry3D->SetImageGeometry(true);
// Set the rotation to zero (identity times spacing matrix) because the default makes a rotation
mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform();
mitk::AffineTransform3D::MatrixType matrix;
matrix[0][0] = spacing[0];
matrix[0][1] = 0.0;
matrix[0][2] = 0.0;
matrix[1][0] = 0.0;
matrix[1][1] = spacing[1];
matrix[1][2] = 0.0;
matrix[2][0] = 0.0;
matrix[2][1] = 0.0;
matrix[2][2] = spacing[2];
geom->SetMatrix(matrix);
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(rotatedSlicedGeometry3D.GetPointer()));
// construct the planes in y direction
{
// first Slice in Y direction
mitk::Point3D originFirstSlice;
originFirstSlice[0] = 0.0;
originFirstSlice[1] = -1.0;
originFirstSlice[2] = 0.0;
mitk::Vector3D normalFitrstSlice;
mitk::FillVector3D(normalFitrstSlice, 0.0, 1.0, 0.0);
mitk::Vector3D spacingFirstSlice;
spacingFirstSlice[0] = 1.0;
spacingFirstSlice[1] = 1.0;
spacingFirstSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New();
planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice);
planeOnFirstSlice->SetSpacing(spacingFirstSlice);
// last Slice in Y Direction
mitk::Point3D originLastSlice;
originLastSlice[0] = 0.0;
originLastSlice[1] = 99.0;
originLastSlice[2] = 0.0;
mitk::Vector3D normalLastSlice;
mitk::FillVector3D(normalLastSlice, 0.0, 1.0, 0.0);
mitk::Vector3D spacingLastSlice;
spacingLastSlice[0] = 1.0;
spacingLastSlice[1] = 1.0;
spacingLastSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New();
planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice);
planeOnLastSlice->SetSpacing(spacingLastSlice);
// calculate the intersection on the last slice
calculator->SetInput(planeOnLastSlice, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
// calculate the intersection on the first slice
calculator->SetInput(planeOnFirstSlice, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
}
// now we make a rotation of the Image about 270 degrees around Z Axis to get an alignment on the positive x axis
double angleInDegrees = 270 * itk::Math::pi / 180;
mitk::AffineTransform3D::MatrixType matrix2;
matrix2[0][0] = cos(angleInDegrees);
matrix2[0][1] = -sin(angleInDegrees);
matrix2[0][2] = 0.0;
matrix2[1][0] = sin(angleInDegrees);
matrix2[1][1] = cos(angleInDegrees);
matrix2[1][2] = 0.0;
matrix2[2][0] = 0.0;
matrix2[2][1] = 0.0;
matrix2[2][2] = 1.0;
// multiplie the identity with the transformation matrix
mitk::AffineTransform3D::MatrixType TransformationMatrix = matrix2 * matrix;
// initialize the image with the new rotation Matrix
geom->SetMatrix(TransformationMatrix);
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(rotatedSlicedGeometry3D.GetPointer()));
{
- // first Slice in X Diection
+ // first Slice in X Direction
mitk::Point3D originFirstSlice;
originFirstSlice[0] = -1.0;
originFirstSlice[1] = 0.0;
originFirstSlice[2] = 0.0;
mitk::Vector3D normalFitrstSlice;
mitk::FillVector3D(normalFitrstSlice, 1.0, 0.0, 0.0);
mitk::Vector3D spacingFirstSlice;
spacingFirstSlice[0] = 1.0;
spacingFirstSlice[1] = 1.0;
spacingFirstSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New();
planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice);
planeOnFirstSlice->SetSpacing(spacingFirstSlice);
// last Slice in X Direction
mitk::Point3D originLastSlice;
originLastSlice[0] = 99.0;
originLastSlice[1] = 0.0;
originLastSlice[2] = 0.0;
mitk::Vector3D normalLastSlice;
mitk::FillVector3D(normalLastSlice, 1.0, 0.0, 0.0);
mitk::Vector3D spacingLastSlice;
spacingLastSlice[0] = 1.0;
spacingLastSlice[1] = 1.0;
spacingLastSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New();
planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice);
planeOnLastSlice->SetSpacing(spacingLastSlice);
calculator->SetInput(planeOnLastSlice, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
calculator->SetInput(planeOnFirstSlice, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
}
delete calculator;
}
int mitkClippedSurfaceBoundsCalculatorTest(int, char *[])
{
// always start with this!
MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator");
/** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a
* Geometry3D.
* This unit test checks if the correct min and max values for the three spatial directions (x, y, z)
* are calculated. To test this we define artificial PlaneGeometries and Geometry3Ds and test different
* scenarios:
*
* 1. planes which are inside the bounding box of a 3D geometry but only on one slice
* 2. planes which are outside of the bounding box
* 3. planes which are inside the bounding box but over more than one slice
*
* Note: Currently rotated geometries are not tested!
*/
/********************* Define Geometry3D ***********************/
// Define origin:
mitk::Point3D origin;
origin[0] = 511;
origin[1] = 0;
origin[2] = 0;
// Define normal:
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0, 0, 1);
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New();
planeGeometry->InitializePlane(origin, normal);
// Set Bounds:
mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds();
bounds[0] = 0;
bounds[1] = 512;
bounds[2] = 0;
bounds[3] = 512;
bounds[4] = 0;
bounds[5] = 1;
planeGeometry->SetBounds(bounds);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New();
slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry.GetPointer()), 20);
mitk::BaseGeometry::Pointer geometry3D = dynamic_cast<mitk::BaseGeometry *>(slicedGeometry3D.GetPointer());
geometry3D->SetImageGeometry(true);
// Define origin for second Geometry3D;
mitk::Point3D origin2;
origin2[0] = 511;
origin2[1] = 60;
origin2[2] = 0;
// Define normal:
mitk::Vector3D normal2;
mitk::FillVector3D(normal2, 0, 1, 0);
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New();
planeGeometry2->InitializePlane(origin2, normal2);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New();
secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry2.GetPointer()), 20);
mitk::BaseGeometry::Pointer secondGeometry3D =
dynamic_cast<mitk::BaseGeometry *>(secondSlicedGeometry3D.GetPointer());
secondGeometry3D->SetImageGeometry(true);
/***************************************************************/
CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D);
CheckPlanesOutsideOfBoundingBox(geometry3D);
CheckPlanesInsideBoundingBox(geometry3D);
CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D);
CheckIntersectionWithPointCloud(geometry3D);
CheckIntersectionWithRotatedGeometry();
CheckIntersectionWithRotatedGeometry90();
/** ToDo:
* test also rotated 3D geometry!
*/
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkDataStorageTest.cpp b/Modules/Core/test/mitkDataStorageTest.cpp
index 81a5e0588e..393f05ac75 100644
--- a/Modules/Core/test/mitkDataStorageTest.cpp
+++ b/Modules/Core/test/mitkDataStorageTest.cpp
@@ -1,873 +1,873 @@
/*============================================================================
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 <algorithm>
#include <fstream>
#include "mitkColorProperty.h"
#include "mitkDataNode.h"
#include "mitkGroupTagProperty.h"
#include "mitkImage.h"
#include "mitkReferenceCountWatcher.h"
#include "mitkStringProperty.h"
#include "mitkSurface.h"
#include "mitkDataStorage.h"
#include "mitkIOUtil.h"
#include "mitkMessage.h"
#include "mitkNodePredicateAnd.h"
#include "mitkNodePredicateData.h"
#include "mitkNodePredicateDataType.h"
#include "mitkNodePredicateDimension.h"
#include "mitkNodePredicateNot.h"
#include "mitkNodePredicateOr.h"
#include "mitkNodePredicateProperty.h"
#include "mitkStandaloneDataStorage.h"
//#include "mitkPicFileReader.h"
#include "mitkTestingMacros.h"
void TestDataStorage(mitk::DataStorage *ds, std::string filename);
namespace mitk
{
class TestStandaloneDataStorage : public StandaloneDataStorage
{
public:
mitkClassMacro(TestStandaloneDataStorage, mitk::DataStorage);
itkFactorylessNewMacro(Self)
itkCloneMacro(Self) std::map<const mitk::DataNode *, unsigned long> GetModifiedObserverTags() const
{
return m_NodeModifiedObserverTags;
}
std::map<const mitk::DataNode *, unsigned long> GetDeletedObserverTags() const { return m_NodeDeleteObserverTags; }
protected:
TestStandaloneDataStorage() {}
};
}
class DSEventReceiver // Helper class for event testing
{
public:
const mitk::DataNode *m_NodeAdded;
const mitk::DataNode *m_NodeRemoved;
DSEventReceiver() : m_NodeAdded(nullptr), m_NodeRemoved(nullptr) {}
void OnAdd(const mitk::DataNode *node) { m_NodeAdded = node; }
void OnRemove(const mitk::DataNode *node) { m_NodeRemoved = node; }
};
///
/// \brief a class for checking if the datastorage is really thread safe
///
/// Therefore it listens to a node contained in the datastorage. when this node
/// gets removed and deleted, this class gets informed by calling OnObjectDelete().
/// in OnObjectDelete() an empty node gets added. this must not cause a deadlock
///
struct ItkDeleteEventListener
{
ItkDeleteEventListener(mitk::DataStorage *ds) : m_Node(nullptr), m_DataStorage(ds), m_DeleteObserverTag(0) {}
void SetNode(mitk::DataNode *_Node)
{
if (m_Node)
return;
m_Node = _Node;
itk::MemberCommand<ItkDeleteEventListener>::Pointer onObjectDelete =
itk::MemberCommand<ItkDeleteEventListener>::New();
onObjectDelete->SetCallbackFunction(this, &ItkDeleteEventListener::OnObjectDelete);
m_DeleteObserverTag = m_Node->AddObserver(itk::DeleteEvent(), onObjectDelete);
}
void OnObjectDelete(const itk::Object * /*caller*/, const itk::EventObject &)
{
mitk::DataNode::Pointer node = mitk::DataNode::New();
m_DataStorage->Add(node); // SHOULD NOT CAUSE A DEADLOCK!
m_DataStorage->Remove(node); // tidy up: remove the empty node again
m_Node = nullptr;
}
protected:
mitk::DataNode *m_Node;
mitk::DataStorage::Pointer m_DataStorage;
unsigned int m_DeleteObserverTag;
};
//## Documentation
//## main testing method
//## NOTE: the current Singleton implementation of DataTreeStorage will lead to crashes if a testcase fails
//## and therefore mitk::DataStorage::ShutdownSingleton() is not called.
int mitkDataStorageTest(int argc, char *argv[])
{
MITK_TEST_BEGIN("DataStorageTest");
// muellerm: test observer tag remove
mitk::TestStandaloneDataStorage::Pointer testDS = mitk::TestStandaloneDataStorage::New();
mitk::DataNode::Pointer n1 = mitk::DataNode::New();
testDS->Add(n1);
MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 1,
"Testing if modified"
" observer was added.");
MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 1,
"Testing if delete"
" observer was added.");
testDS->Remove(n1);
MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 0,
"Testing if modified"
" observer was removed.");
MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 0,
"Testing if delete"
" observer was removed.");
/* Create StandaloneDataStorage */
MITK_TEST_OUTPUT(<< "Create StandaloneDataStorage : ");
mitk::StandaloneDataStorage::Pointer sds;
try
{
sds = mitk::StandaloneDataStorage::New();
- MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instatiation");
+ MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instantiation");
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during creation of StandaloneDataStorage");
}
MITK_TEST_OUTPUT(<< "Testing StandaloneDataStorage: ");
MITK_TEST_CONDITION_REQUIRED(argc > 1, "Testing correct test invocation");
TestDataStorage(sds, argv[1]);
// TODO: Add specific StandaloneDataStorage Tests here
sds = nullptr;
MITK_TEST_END();
}
//##Documentation
//## @brief Test for the DataStorage class and its associated classes (e.g. the predicate classes)
//## This method will be called once for each subclass of DataStorage
void TestDataStorage(mitk::DataStorage *ds, std::string filename)
{
/* DataStorage valid? */
MITK_TEST_CONDITION_REQUIRED(ds != nullptr, "DataStorage valid?");
// Take the ItkImageFile Reader for the .nrrd data format.
// (was previously pic which is now deprecated format)
mitk::Image::Pointer image = mitk::IOUtil::Load<mitk::Image>(filename);
// create some DataNodes to fill the ds
mitk::DataNode::Pointer n1 = mitk::DataNode::New(); // node with image and name property
// mitk::Image::Pointer image = mitk::Image::New();
// unsigned int imageDimensions[] = { 10, 10, 10, 10 };
// mitk::PixelType pt(typeid(int));
// image->Initialize( pt, 4, imageDimensions );
n1->SetData(image);
n1->SetProperty("name", mitk::StringProperty::New("Node 1 - Image Node"));
mitk::DataStorage::SetOfObjects::Pointer parents1 = mitk::DataStorage::SetOfObjects::New();
mitk::DataNode::Pointer n2 = mitk::DataNode::New(); // node with surface and name and color properties
mitk::Surface::Pointer surface = mitk::Surface::New();
n2->SetData(surface);
n2->SetProperty("name", mitk::StringProperty::New("Node 2 - Surface Node"));
mitk::Color color;
color.Set(1.0f, 1.0f, 0.0f);
n2->SetColor(color);
n2->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New());
mitk::DataStorage::SetOfObjects::Pointer parents2 = mitk::DataStorage::SetOfObjects::New();
parents2->InsertElement(0, n1); // n1 (image node) is source of n2 (surface node)
mitk::DataNode::Pointer n3 = mitk::DataNode::New(); // node without data but with name property
n3->SetProperty("name", mitk::StringProperty::New("Node 3 - Empty Node"));
n3->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New());
n3->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New());
mitk::DataStorage::SetOfObjects::Pointer parents3 = mitk::DataStorage::SetOfObjects::New();
parents3->InsertElement(0, n2); // n2 is source of n3
mitk::DataNode::Pointer n4 = mitk::DataNode::New(); // node without data but with color property
n4->SetColor(color);
n4->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New());
mitk::DataStorage::SetOfObjects::Pointer parents4 = mitk::DataStorage::SetOfObjects::New();
parents4->InsertElement(0, n2);
parents4->InsertElement(1, n3); // n2 and n3 are sources of n4
mitk::DataNode::Pointer n5 = mitk::DataNode::New(); // extra node
n5->SetProperty("name", mitk::StringProperty::New("Node 5"));
try /* adding objects */
{
/* Add an object */
ds->Add(n1, parents1);
MITK_TEST_CONDITION_REQUIRED((ds->GetAll()->Size() == 1) && (ds->GetAll()->GetElement(0) == n1),
"Testing Adding a new object");
/* Check exception on adding the same object again */
MITK_TEST_OUTPUT(<< "Check exception on adding the same object again: ");
MITK_TEST_FOR_EXCEPTION(std::exception, ds->Add(n1, parents1));
MITK_TEST_CONDITION(ds->GetAll()->Size() == 1, "Test if object count is correct after exception");
/* Add an object that has a source object */
ds->Add(n2, parents2);
MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 2, "Testing Adding an object that has a source object");
/* Add some more objects needed for further tests */
ds->Add(n3, parents3); // n3 object that has name property and one parent
ds->Add(n4, parents4); // n4 object that has color property
ds->Add(n5); // n5 has no parents
MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 5, "Adding some more objects needed for further tests");
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during object creation");
}
try /* object retrieval methods */
{
/* Requesting all Objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll();
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((stlAll.size() == 5) // check if all tree nodes are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()),
"Testing GetAll()");
}
/* Requesting a named object */
{
mitk::NodePredicateProperty::Pointer predicate(
mitk::NodePredicateProperty::New("name", mitk::StringProperty::New("Node 2 - Surface Node")));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting a named object");
}
/* Requesting objects of specific data type */
{
mitk::NodePredicateDataType::Pointer predicate(mitk::NodePredicateDataType::New("Image"));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific data type")
}
/* Requesting objects of specific dimension */
{
mitk::NodePredicateDimension::Pointer predicate(mitk::NodePredicateDimension::New(4));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific dimension")
}
/* Requesting objects with specific data object */
{
mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(image));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1),
"Requesting objects with specific data object")
}
/* Requesting objects with nullptr data */
{
mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(nullptr));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n3) != all->end()) &&
(std::find(all->begin(), all->end(), n4) != all->end()) &&
(std::find(all->begin(), all->end(), n5) != all->end()),
"Requesting objects with nullptr data");
}
/* Requesting objects that meet a conjunction criteria */
{
mitk::NodePredicateDataType::Pointer p1 = mitk::NodePredicateDataType::New("Surface");
mitk::NodePredicateProperty::Pointer p2 =
mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color));
mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New();
predicate->AddPredicate(p1);
predicate->AddPredicate(p2); // objects must be of datatype "Surface" and have red color (= n2)
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2),
"Requesting objects that meet a conjunction criteria");
}
/* Requesting objects that meet a disjunction criteria */
{
mitk::NodePredicateDataType::Pointer p1(mitk::NodePredicateDataType::New("Image"));
mitk::NodePredicateProperty::Pointer p2(
mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color)));
mitk::NodePredicateOr::Pointer predicate = mitk::NodePredicateOr::New();
predicate->AddPredicate(p1);
predicate->AddPredicate(p2); // objects must be of datatype "Surface" or have red color (= n1, n2, n4)
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n1) != all->end()) &&
(std::find(all->begin(), all->end(), n2) != all->end()) &&
(std::find(all->begin(), all->end(), n4) != all->end()),
"Requesting objects that meet a disjunction criteria");
}
/* Requesting objects that do not meet a criteria */
{
mitk::ColorProperty::Pointer cp = mitk::ColorProperty::New(color);
mitk::NodePredicateProperty::Pointer proppred(mitk::NodePredicateProperty::New("color", cp));
mitk::NodePredicateNot::Pointer predicate(mitk::NodePredicateNot::New(proppred));
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 3) // check if correct objects are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()),
"Requesting objects that do not meet a criteria");
}
/* Requesting *direct* source objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetSources(n3, nullptr, true); // Get direct parents of n3 (=n2)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"Requesting *direct* source objects");
}
/* Requesting *all* source objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetSources(n3, nullptr, false); // Get all parents of n3 (= n1 + n2)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"Requesting *all* source objects"); // check if n1 and n2 are the resultset
}
/* Requesting *all* sources of object with multiple parents */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetSources(n4, nullptr, false); // Get all parents of n4 (= n1 + n2 + n3)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION(
(all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset
,
"Requesting *all* sources of object with multiple parents");
}
/* Requesting *direct* derived objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetDerivations(n1, nullptr, true); // Get direct childs of n1 (=n2)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) !=
stlAll.end()) // check if n1 is the resultset
,
"Requesting *direct* derived objects");
}
///* Requesting *direct* derived objects with multiple parents/derivations */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetDerivations(n2, nullptr, true); // Get direct childs of n2 (=n3 + n4)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION(
(all->Size() == 2) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n3 is the resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) // check if n4 is the resultset
,
"Requesting *direct* derived objects with multiple parents/derivations");
}
//* Requesting *all* derived objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetDerivations(n1, nullptr, false); // Get all childs of n1 (=n2, n3, n4)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()),
"Requesting *all* derived objects");
}
/* Checking for circular source relationships */
{
parents1->InsertElement(0, n4); // make n1 derived from n4 (which is derived from n2, which is derived from n1)
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(
n4,
nullptr,
false); // Get all parents of n4 (= n1 + n2 + n3, not n4 itself and not multiple versions of the nodes!)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION(
(all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset
,
"Checking for circular source relationships");
}
///* Checking for circular derivation relationships can not be performed, because the internal derivations
/// datastructure
// can not be accessed from the outside. (Therefore it should not be possible to create these circular relations
// */
//* Checking GroupTagProperty */
{
mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New();
mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 1", tp));
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n3 are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()),
"Checking GroupTagProperty");
}
/* Checking GroupTagProperty 2 */
{
mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New();
mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 2", tp));
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) // check if n3 and n4 are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()),
"Checking GroupTagProperty 2");
}
/* Checking direct sources with condition */
{
mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Surface");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, true);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"checking direct sources with condition");
}
/* Checking all sources with condition */
{
mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Image");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) // check if n1 is in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()),
"Checking all sources with condition");
}
/* Checking all sources with condition with empty resultset */
{
mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("VesselTree");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false);
MITK_TEST_CONDITION(all->Size() == 0,
"Checking all sources with condition with empty resultset"); // check if resultset is empty
}
/* Checking direct derivations with condition */
{
mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, true);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"Checking direct derivations with condition");
}
/* Checking all derivations with condition */
{
mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, false);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n4 are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()),
"Checking direct derivations with condition");
}
/* Checking named node method */
MITK_TEST_CONDITION(ds->GetNamedNode("Node 2 - Surface Node") == n2, "Checking named node method");
MITK_TEST_CONDITION(ds->GetNamedNode(std::string("Node 2 - Surface Node")) == n2,
"Checking named node(std::string) method");
/* Checking named node method with wrong name */
MITK_TEST_CONDITION(ds->GetNamedNode("This name does not exist") == nullptr,
"Checking named node method with wrong name");
/* Checking named object method */
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Image>("Node 1 - Image Node") == image,
"Checking named object method");
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Image>(std::string("Node 1 - Image Node")) == image,
"Checking named object(std::string) method");
/* Checking named object method with wrong DataType */
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Surface>("Node 1 - Image Node") == nullptr,
"Checking named object method with wrong DataType");
/* Checking named object method with wrong name */
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Image>("This name does not exist") == nullptr,
"Checking named object method with wrong name");
/* Checking GetNamedDerivedNode with valid name and direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 2 - Surface Node", n1, true) == n2,
"Checking GetNamedDerivedNode with valid name & direct derivation only");
/* Checking GetNamedDerivedNode with invalid Name and direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("wrong name", n1, true) == nullptr,
"Checking GetNamedDerivedNode with invalid name & direct derivation only");
/* Checking GetNamedDerivedNode with invalid Name and direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, false) == n3,
"Checking GetNamedDerivedNode with invalid name & direct derivation only");
/* Checking GetNamedDerivedNode with valid Name but direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, true) == nullptr,
"Checking GetNamedDerivedNode with valid Name but direct derivation only");
/* Checking GetNode with valid predicate */
{
mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("Image"));
MITK_TEST_CONDITION(ds->GetNode(p) == n1, "Checking GetNode with valid predicate");
}
/* Checking GetNode with invalid predicate */
{
mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("PointSet"));
MITK_TEST_CONDITION(ds->GetNode(p) == nullptr, "Checking GetNode with invalid predicate");
}
} // object retrieval methods
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during object retrieval (GetXXX() Methods)");
}
try /* object removal methods */
{
/* Checking removal of a node without relations */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
ds->Add(extra);
MITK_TEST_CONDITION(ds->GetNamedNode("extra") == extra, "Adding extra node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()),
"Checking removal of a node without relations");
extra = nullptr;
}
/* Checking removal of a node with a parent */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
ds->Add(extra, n1); // n1 is parent of extra
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) &&
(ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1
,
"Adding extra node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetDerivations(n1)->Size() == 1),
"Checking removal of a node with a parent");
extra = nullptr;
}
/* Checking removal of a node with two parents */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(n2);
ds->Add(extra, p); // n1 and n2 are parents of extra
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) &&
(ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 3),
"add extra node");
ds->Remove(extra);
MITK_TEST_CONDITION(
(ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2
,
"Checking removal of a node with two parents");
extra = nullptr;
}
/* Checking removal of a node with two derived nodes */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
ds->Add(extra);
mitk::DataNode::Pointer d1 = mitk::DataNode::New();
d1->SetProperty("name", mitk::StringProperty::New("d1"));
ds->Add(d1, extra);
mitk::DataNode::Pointer d2 = mitk::DataNode::New();
d2->SetProperty("name", mitk::StringProperty::New("d2"));
ds->Add(d2, extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1") == d1) &&
(ds->GetNamedNode("d2") == d2) &&
(ds->GetSources(d1)->Size() == 1) // extra should be source of d1
&&
(ds->GetSources(d2)->Size() == 1) // extra should be source of d2
&&
(ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra
,
"add extra node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1") == d1) &&
(ds->GetNamedNode("d2") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore
&&
(ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore
,
"Checking removal of a node with two derived nodes");
extra = nullptr;
}
/* Checking removal of a node with two parents and two derived nodes */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
mitk::ReferenceCountWatcher::Pointer n1watcher = new mitk::ReferenceCountWatcher(n1);
int refCountbeforeDS = watcher->GetReferenceCount();
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(n2);
ds->Add(extra, p); // n1 and n2 are parents of extra
mitk::DataNode::Pointer d1 = mitk::DataNode::New();
d1->SetProperty("name", mitk::StringProperty::New("d1x"));
ds->Add(d1, extra);
mitk::DataNode::Pointer d2 = mitk::DataNode::New();
d2->SetProperty("name", mitk::StringProperty::New("d2x"));
ds->Add(d2, extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1x") == d1) &&
(ds->GetNamedNode("d2x") == d2) &&
(ds->GetSources(d1)->Size() == 1) // extra should be source of d1
&&
(ds->GetSources(d2)->Size() == 1) // extra should be source of d2
&&
(ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 3) // n3, n4 and extra should be derived from n2
&&
(ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra
,
"add extra node");
ds->Remove(extra);
MITK_TEST_CONDITION(
(ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) &&
(refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2
&&
(ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore
&&
(ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore
,
"Checking removal of a node with two parents and two derived nodes");
extra = nullptr;
}
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during object removal methods");
}
/* Checking for node is it's own parent exception */
{
MITK_TEST_FOR_EXCEPTION_BEGIN(std::exception);
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(extra); // extra is parent of extra!!!
ds->Add(extra, p);
MITK_TEST_FOR_EXCEPTION_END(std::exception);
}
/* Checking reference count of node after add and remove */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(n3);
ds->Add(extra, p);
extra = nullptr;
ds->Remove(ds->GetNamedNode("extra"));
MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Checking reference count of node after add and remove");
}
/* Checking removal of a node with two derived nodes [ dataStorage->GetDerivations( rootNode )] see bug #3426 */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
ds->Add(extra);
mitk::DataNode::Pointer d1y = mitk::DataNode::New();
d1y->SetProperty("name", mitk::StringProperty::New("d1y"));
mitk::ReferenceCountWatcher::Pointer watcherD1y = new mitk::ReferenceCountWatcher(d1y);
int refCountbeforeDS = watcherD1y->GetReferenceCount();
ds->Add(d1y, extra);
mitk::DataNode::Pointer d2y = mitk::DataNode::New();
d2y->SetProperty("name", mitk::StringProperty::New("d2y"));
ds->Add(d2y, extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == d1y) &&
(ds->GetNamedNode("d2y") == d2y) &&
(ds->GetSources(d1y)->Size() == 1) // extra should be source of d1y
&&
(ds->GetSources(d2y)->Size() == 1) // extra should be source of d2y
&&
(ds->GetDerivations(extra)->Size() == 2) // d1y and d2y should be derived from extra
,
"add extra node");
ds->Remove(ds->GetDerivations(extra));
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) &&
(ds->GetNamedNode("d1y") == nullptr) // d1y should be nullptr now
&&
(ds->GetNamedNode("d2y") == nullptr) // d2y should be nullptr now
&&
(refCountbeforeDS == watcherD1y->GetReferenceCount()),
"Checking removal of subset of two derived nodes from one parent node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr), "Checking removal of a parent node");
extra = nullptr;
}
/* Checking GetGrouptags() */
{
const std::set<std::string> groupTags = ds->GetGroupTags();
MITK_TEST_CONDITION((groupTags.size() == 2) &&
(std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 1") != groupTags.end()) &&
(std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 2") != groupTags.end()),
"Checking GetGrouptags()");
}
/* Checking Event handling */
DSEventReceiver listener;
try
{
ds->AddNodeEvent +=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnAdd);
ds->RemoveNodeEvent +=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnRemove);
mitk::DataNode::Pointer extra = mitk::DataNode::New();
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
ds->Add(extra);
MITK_TEST_CONDITION(listener.m_NodeAdded == extra.GetPointer(), "Checking AddEvent");
ds->Remove(extra);
MITK_TEST_CONDITION(listener.m_NodeRemoved == extra.GetPointer(), "Checking RemoveEvent");
/* RemoveListener */
ds->AddNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnAdd);
ds->RemoveNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnRemove);
listener.m_NodeAdded = nullptr;
listener.m_NodeRemoved = nullptr;
ds->Add(extra);
ds->Remove(extra);
MITK_TEST_CONDITION((listener.m_NodeRemoved == nullptr) && (listener.m_NodeAdded == nullptr), "Checking RemoveListener");
std::cout << "Pointer handling after event handling: " << std::flush;
extra = nullptr; // delete reference to the node. its memory should be freed now
MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Pointer handling after event handling");
}
catch (...)
{
/* cleanup */
ds->AddNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnAdd);
ds->RemoveNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnRemove);
MITK_TEST_FAILED_MSG(<< "Exception during object removal methods");
}
// Checking ComputeBoundingGeometry3D method*/
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll();
auto geometry = ds->ComputeBoundingGeometry3D();
MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4, "Test for number or time steps with ComputeBoundingGeometry()");
mitk::TimeBounds timebounds = geometry->GetTimeBounds();
MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4),
"Test for timebounds with ComputeBoundingGeometry()");
for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++)
{
mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i);
mitk::TimeBounds bounds = geometry->GetTimeBounds(i);
MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1),
"Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()");
}
geometry = ds->ComputeBoundingGeometry3D(all);
MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4,
"Test for number or time steps with ComputeBoundingGeometry(allNodes)");
timebounds = geometry->GetTimeBounds();
MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4),
"Test for timebounds with ComputeBoundingGeometry(allNodes)");
for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++)
{
mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i);
mitk::TimeBounds bounds = geometry->GetTimeBounds(i);
MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1),
"Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()");
}
// test for thread safety of DataStorage
try
{
mitk::StandaloneDataStorage::Pointer standaloneDataStorage = mitk::StandaloneDataStorage::New();
ItkDeleteEventListener listener(standaloneDataStorage);
{
mitk::DataNode::Pointer emptyNode = mitk::DataNode::New();
mitk::DataNode *pEmptyNode = emptyNode;
listener.SetNode(emptyNode);
standaloneDataStorage->Add(emptyNode);
emptyNode = nullptr; // emptyNode is still alive because standaloneDataStorage
// owns it
standaloneDataStorage->Remove(pEmptyNode); // this should not freeze the whole thing
}
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during testing DataStorage thread safe");
}
/* Clear DataStorage */
ds->Remove(ds->GetAll());
MITK_TEST_CONDITION(ds->GetAll()->Size() == 0, "Checking Clear DataStorage");
}
diff --git a/Modules/Core/test/mitkExtractSliceFilterTest.cpp b/Modules/Core/test/mitkExtractSliceFilterTest.cpp
index afe25f5d1f..d371bdbf31 100644
--- a/Modules/Core/test/mitkExtractSliceFilterTest.cpp
+++ b/Modules/Core/test/mitkExtractSliceFilterTest.cpp
@@ -1,1069 +1,1069 @@
/*============================================================================
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 <itkImage.h>
#include <itkImageRegionIterator.h>
#include <mitkExtractSliceFilter.h>
#include <mitkIOUtil.h>
#include <mitkITKImageImport.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkInteractionConst.h>
#include <mitkNumericTypes.h>
#include <mitkRotationOperation.h>
#include <mitkStandardFileLocations.h>
#include <mitkTestingMacros.h>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <mitkGeometry3D.h>
#include <vtkActor.h>
#include <vtkImageActor.h>
#include <vtkImageData.h>
#include <vtkImageMapToColors.h>
#include <vtkImageMapper.h>
#include <vtkImageReslice.h>
#include <vtkInteractorStyleImage.h>
#include <vtkLookupTable.h>
#include <vtkPlaneSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkTexture.h>
// use this to create the test volume on the fly
#define CREATE_VOLUME
// use this to save the created volume
//#define SAVE_VOLUME
// use this to calculate the error from the sphere mathematical model to our pixel based one
//#define CALC_TESTFAILURE_DEVIATION
// use this to render an oblique slice through a specified image
//#define SHOW_SLICE_IN_RENDER_WINDOW
// use this to have infos printed in mitkLog
//#define EXTRACTOR_DEBUG
/*these are the deviations calculated by the function CalcTestFailureDeviation (see for details)*/
#define Testfailure_Deviation_Mean_128 0.853842
#define Testfailure_Deviation_Volume_128 0.145184
#define Testfailure_Deviation_Diameter_128 1.5625
#define Testfailure_Deviation_Mean_256 0.397693
#define Testfailure_Deviation_Volume_256 0.0141357
#define Testfailure_Deviation_Diameter_256 0.78125
#define Testfailure_Deviation_Mean_512 0.205277
#define Testfailure_Deviation_Volume_512 0.01993
#define Testfailure_Deviation_Diameter_512 0.390625
class mitkExtractSliceFilterTestClass
{
public:
static void TestSlice(mitk::PlaneGeometry *planeGeometry, std::string testname)
{
TestPlane = planeGeometry;
TestName = testname;
mitk::ScalarType centerCoordValue = TestvolumeSize / 2.0;
mitk::ScalarType center[3] = {centerCoordValue, centerCoordValue, centerCoordValue};
mitk::Point3D centerIndex(center);
double radius = TestvolumeSize / 4.0;
if (TestPlane->Distance(centerIndex) >= radius)
return; // outside sphere
// feed ExtractSliceFilter
mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New();
slicer->SetInput(TestVolume);
slicer->SetWorldGeometry(TestPlane);
slicer->Update();
MITK_TEST_CONDITION_REQUIRED(slicer->GetOutput() != nullptr, "Extractor returned a slice");
mitk::Image::Pointer reslicedImage = slicer->GetOutput();
AccessFixedDimensionByItk(reslicedImage, TestSphereRadiusByItk, 2);
AccessFixedDimensionByItk(reslicedImage, TestSphereAreaByItk, 2);
/*
double devArea, devDiameter;
if(TestvolumeSize == 128.0){ devArea = Testfailure_Deviation_Volume_128; devDiameter =
Testfailure_Deviation_Diameter_128; }
else if(TestvolumeSize == 256.0){devArea = Testfailure_Deviation_Volume_256; devDiameter =
Testfailure_Deviation_Diameter_256;}
else if (TestvolumeSize == 512.0){devArea = Testfailure_Deviation_Volume_512; devDiameter =
Testfailure_Deviation_Diameter_512;}
else{devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128;}
*/
std::string areatestName = TestName.append(" area");
std::string diametertestName = TestName.append(" testing diameter");
// TODO think about the deviation, 1% makes no sense at all
MITK_TEST_CONDITION(std::abs(100 - testResults.percentageAreaCalcToPixel) < 1, areatestName);
MITK_TEST_CONDITION(std::abs(100 - testResults.percentageRadiusToPixel) < 1, diametertestName);
#ifdef EXTRACTOR_DEBUG
MITK_INFO << TestName << " >>> "
<< "planeDistanceToSphereCenter: " << testResults.planeDistanceToSphereCenter;
MITK_INFO << "area in pixels: " << testResults.areaInPixel << " <-> area in mm: " << testResults.areaCalculated
<< " = " << testResults.percentageAreaCalcToPixel << "%";
MITK_INFO << "calculated diameter: " << testResults.diameterCalculated
<< " <-> diameter in mm: " << testResults.diameterInMM
<< " <-> diameter in pixel: " << testResults.diameterInPixel << " = "
<< testResults.percentageRadiusToPixel << "%";
#endif
}
/*
* get the radius of the slice of a sphere based on pixel distance from edge to edge of the circle.
*/
template <typename TPixel, unsigned int VImageDimension>
static void TestSphereRadiusByItk(itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
// set the index to the middle of the image's edge at x and y axis
typename InputImageType::IndexType currentIndexX;
currentIndexX[0] = (int)(TestvolumeSize / 2.0);
currentIndexX[1] = 0;
typename InputImageType::IndexType currentIndexY;
currentIndexY[0] = 0;
currentIndexY[1] = (int)(TestvolumeSize / 2.0);
// remember the last pixel value
double lastValueX = inputImage->GetPixel(currentIndexX);
double lastValueY = inputImage->GetPixel(currentIndexY);
// storage for the index marks
std::vector<typename InputImageType::IndexType> indicesX;
std::vector<typename InputImageType::IndexType> indicesY;
/*Get four indices on the edge of the circle*/
while (currentIndexX[1] < TestvolumeSize && currentIndexX[0] < TestvolumeSize)
{
// move x direction
currentIndexX[1] += 1;
// move y direction
currentIndexY[0] += 1;
if (inputImage->GetPixel(currentIndexX) > lastValueX)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexX[0];
markIndex[1] = currentIndexX[1];
indicesX.push_back(markIndex);
}
else if (inputImage->GetPixel(currentIndexX) < lastValueX)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexX[0];
markIndex[1] = currentIndexX[1] - 1; // value inside the sphere
indicesX.push_back(markIndex);
}
if (inputImage->GetPixel(currentIndexY) > lastValueY)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexY[0];
markIndex[1] = currentIndexY[1];
indicesY.push_back(markIndex);
}
else if (inputImage->GetPixel(currentIndexY) < lastValueY)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexY[0];
markIndex[1] = currentIndexY[1] - 1; // value inside the sphere
indicesY.push_back(markIndex);
}
// found both marks?
if (indicesX.size() == 2 && indicesY.size() == 2)
break;
// the new 'last' values
lastValueX = inputImage->GetPixel(currentIndexX);
lastValueY = inputImage->GetPixel(currentIndexY);
}
/*
*If we are here we found the four marks on the edge of the circle.
*For the case our plane is rotated and shifted, we have to calculate the center of the circle,
*else the center is the intersection of both straight lines between the marks.
*When we have the center, the diameter of the circle will be checked to the reference value(math!).
*/
// each distance from the first mark of each direction to the center of the straight line between the marks
double distanceToCenterX = std::abs(indicesX[0][1] - indicesX[1][1]) / 2.0;
// double distanceToCenterY = std::abs(indicesY[0][0] - indicesY[1][0]) / 2.0;
// the center of the straight lines
typename InputImageType::IndexType centerX;
// typename InputImageType::IndexType centerY;
centerX[0] = indicesX[0][0];
centerX[1] = indicesX[0][1] + distanceToCenterX;
// TODO think about implicit cast to int. this is not the real center of the image, which could be between two
// pixels
// centerY[0] = indicesY[0][0] + distanceToCenterY;
// centerY[1] = inidcesY[0][1];
typename InputImageType::IndexType currentIndex(centerX);
lastValueX = inputImage->GetPixel(currentIndex);
long sumpixels = 0;
std::vector<typename InputImageType::IndexType> diameterIndices;
// move up
while (currentIndex[1] < TestvolumeSize)
{
currentIndex[1] += 1;
if (inputImage->GetPixel(currentIndex) != lastValueX)
{
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndex[0];
markIndex[1] = currentIndex[1] - 1;
diameterIndices.push_back(markIndex);
break;
}
sumpixels++;
lastValueX = inputImage->GetPixel(currentIndex);
}
currentIndex[1] -= sumpixels; // move back to center to go in the other direction
lastValueX = inputImage->GetPixel(currentIndex);
// move down
while (currentIndex[1] >= 0)
{
currentIndex[1] -= 1;
if (inputImage->GetPixel(currentIndex) != lastValueX)
{
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndex[0];
markIndex[1] = currentIndex[1]; // outside sphere because we want to calculate the distance from edge to edge
diameterIndices.push_back(markIndex);
break;
}
sumpixels++;
lastValueX = inputImage->GetPixel(currentIndex);
}
/*
*Now sumpixels should be the apromximate diameter of the circle. This is checked with the calculated diameter from
*the plane transformation(math).
*/
mitk::Point3D volumeCenter;
volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0;
double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter);
double sphereRadius = TestvolumeSize / 4.0;
// calculate the radius of the circle cut from the sphere by the plane
double diameter = 2.0 * std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2));
double percentageRadiusToPixel = 100 / diameter * sumpixels;
/*
*calculate the radius in mm by the both marks of the center line by using the world coordinates
*/
// get the points as 3D coordinates
mitk::Vector3D diameterPointRight, diameterPointLeft;
diameterPointRight[2] = diameterPointLeft[2] = 0.0;
diameterPointLeft[0] = diameterIndices[0][0];
diameterPointLeft[1] = diameterIndices[0][1];
diameterPointRight[0] = diameterIndices[1][0];
diameterPointRight[1] = diameterIndices[1][1];
// transform to worldcoordinates
TestVolume->GetGeometry()->IndexToWorld(diameterPointLeft, diameterPointLeft);
TestVolume->GetGeometry()->IndexToWorld(diameterPointRight, diameterPointRight);
// euklidian distance
double diameterInMM = ((diameterPointLeft * -1.0) + diameterPointRight).GetNorm();
testResults.diameterInMM = diameterInMM;
testResults.diameterCalculated = diameter;
testResults.diameterInPixel = sumpixels;
testResults.percentageRadiusToPixel = percentageRadiusToPixel;
testResults.planeDistanceToSphereCenter = planeDistanceToSphereCenter;
}
/*brute force the area pixel by pixel*/
template <typename TPixel, unsigned int VImageDimension>
static void TestSphereAreaByItk(itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::ImageRegionConstIterator<InputImageType> ImageIterator;
ImageIterator imageIterator(inputImage, inputImage->GetLargestPossibleRegion());
imageIterator.GoToBegin();
int sumPixelsInArea = 0;
while (!imageIterator.IsAtEnd())
{
if (inputImage->GetPixel(imageIterator.GetIndex()) == pixelValueSet)
sumPixelsInArea++;
++imageIterator;
}
mitk::Point3D volumeCenter;
volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0;
double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter);
double sphereRadius = TestvolumeSize / 4.0;
// calculate the radius of the circle cut from the sphere by the plane
double radius = std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2));
double areaInMM = 3.14159265358979 * std::pow(radius, 2);
testResults.areaCalculated = areaInMM;
testResults.areaInPixel = sumPixelsInArea;
testResults.percentageAreaCalcToPixel = 100 / areaInMM * sumPixelsInArea;
}
/*
- * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel vaues of the voxel
+ * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel values of the voxel
* in the volume with the pixel value in the resliced image.
* there are some indice shifting problems which causes the test to fail for oblique planes. seems like the chosen
* worldcoordinate is not corresponding to the index in the 2D image. and so the pixel values are not the same as
* expected.
*/
static void PixelvalueBasedTest()
{
/* setup itk image */
typedef itk::Image<unsigned short, 3> ImageType;
typedef itk::ImageRegionConstIterator<ImageType> ImageIterator;
ImageType::Pointer image = ImageType::New();
ImageType::IndexType start;
start[0] = start[1] = start[2] = 0;
ImageType::SizeType size;
size[0] = size[1] = size[2] = 32;
ImageType::RegionType imgRegion;
imgRegion.SetSize(size);
imgRegion.SetIndex(start);
image->SetRegions(imgRegion);
image->SetSpacing(mitk::Vector(1.0));
image->Allocate();
ImageIterator imageIterator(image, image->GetLargestPossibleRegion());
imageIterator.GoToBegin();
unsigned short pixelValue = 0;
// fill the image with distinct values
while (!imageIterator.IsAtEnd())
{
image->SetPixel(imageIterator.GetIndex(), pixelValue);
++imageIterator;
++pixelValue;
}
/* end setup itk image */
mitk::Image::Pointer imageInMitk;
CastToMitkImage(image, imageInMitk);
/*mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New();
writer->SetInput(imageInMitk);
std::string file = "C:\\Users\\schroedt\\Desktop\\cube.nrrd";
writer->SetFileName(file);
writer->Update();*/
PixelvalueBasedTestByPlane(imageInMitk, mitk::AnatomicalPlane::Coronal);
PixelvalueBasedTestByPlane(imageInMitk, mitk::AnatomicalPlane::Sagittal);
PixelvalueBasedTestByPlane(imageInMitk, mitk::AnatomicalPlane::Axial);
}
static void PixelvalueBasedTestByPlane(mitk::Image *imageInMitk, mitk::AnatomicalPlane orientation)
{
typedef itk::Image<unsigned short, 3> ImageType;
// set the seed of the rand function
srand((unsigned)time(nullptr));
/* setup a random orthogonal plane */
int sliceindex = 17; // rand() % 32;
bool isFrontside = true;
bool isRotated = false;
if (orientation == mitk::AnatomicalPlane::Axial)
{
/*isFrontside = false;
isRotated = true;*/
}
mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New();
plane->InitializeStandardPlane(imageInMitk->GetGeometry(), orientation, sliceindex, isFrontside, isRotated);
mitk::Point3D origin = plane->GetOrigin();
mitk::Vector3D normal;
normal = plane->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
plane->SetOrigin(origin);
// we dont need this any more, because we are only testing orthogonal planes
/*mitk::Vector3D rotationVector;
rotationVector[0] = randFloat();
rotationVector[1] = randFloat();
rotationVector[2] = randFloat();
float degree = randFloat() * 180.0;
mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, plane->GetCenter(), rotationVector,
degree);
plane->ExecuteOperation(op);
delete op;*/
/* end setup plane */
/* define a point in the 3D volume.
* add the two axis vectors of the plane (each multiplied with a
* random number) to the origin. now the two random numbers
* become our index coordinates in the 2D image, because the
* length of the axis vectors is 1.
*/
mitk::Point3D planeOrigin = plane->GetOrigin();
mitk::Vector3D axis0, axis1;
axis0 = plane->GetAxisVector(0);
axis1 = plane->GetAxisVector(1);
axis0.Normalize();
axis1.Normalize();
unsigned char n1 = 7; // rand() % 32;
unsigned char n2 = 13; // rand() % 32;
mitk::Point3D testPoint3DInWorld;
testPoint3DInWorld = planeOrigin + (axis0 * n1) + (axis1 * n2);
// get the index of the point in the 3D volume
ImageType::IndexType testPoint3DInIndex;
imageInMitk->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint3DInIndex);
itk::Index<3> testPoint2DInIndex;
/* end define a point in the 3D volume.*/
// do reslicing at the plane
mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New();
slicer->SetInput(imageInMitk);
slicer->SetWorldGeometry(plane);
slicer->Update();
mitk::Image::Pointer slice = slicer->GetOutput();
// Get TestPoiont3D as Index in Slice
slice->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint2DInIndex);
mitk::Point3D p, sliceIndexToWorld, imageIndexToWorld;
p[0] = testPoint2DInIndex[0];
p[1] = testPoint2DInIndex[1];
p[2] = testPoint2DInIndex[2];
slice->GetGeometry()->IndexToWorld(p, sliceIndexToWorld);
p[0] = testPoint3DInIndex[0];
p[1] = testPoint3DInIndex[1];
p[2] = testPoint3DInIndex[2];
imageInMitk->GetGeometry()->IndexToWorld(p, imageIndexToWorld);
itk::Index<2> testPoint2DIn2DIndex;
testPoint2DIn2DIndex[0] = testPoint2DInIndex[0];
testPoint2DIn2DIndex[1] = testPoint2DInIndex[1];
typedef mitk::ImagePixelReadAccessor<unsigned short, 3> VolumeReadAccessorType;
typedef mitk::ImagePixelReadAccessor<unsigned short, 2> SliceReadAccessorType;
VolumeReadAccessorType VolumeReadAccessor(imageInMitk);
SliceReadAccessorType SliceReadAccessor(slice);
// compare the pixelvalues of the defined point in the 3D volume with the value of the resliced image
unsigned short valueAt3DVolume = VolumeReadAccessor.GetPixelByIndex(testPoint3DInIndex);
unsigned short valueAtSlice = SliceReadAccessor.GetPixelByIndex(testPoint2DIn2DIndex);
// valueAt3DVolume == valueAtSlice is not always working. because of rounding errors
// indices are shifted
MITK_TEST_CONDITION(valueAt3DVolume == valueAtSlice, "comparing pixelvalues for orthogonal plane");
vtkSmartPointer<vtkImageData> imageInVtk = imageInMitk->GetVtkImageData();
vtkSmartPointer<vtkImageData> sliceInVtk = slice->GetVtkImageData();
double PixelvalueByMitkOutput = sliceInVtk->GetScalarComponentAsDouble(n1, n2, 0, 0);
// double valueVTKinImage = imageInVtk->GetScalarComponentAsDouble(testPoint3DInIndex[0], testPoint3DInIndex[1],
// testPoint3DInIndex[2], 0);
/* Test that everything is working equally if vtkoutput is used instead of the default output
* from mitk ImageToImageFilter
*/
mitk::ExtractSliceFilter::Pointer slicerWithVtkOutput = mitk::ExtractSliceFilter::New();
slicerWithVtkOutput->SetInput(imageInMitk);
slicerWithVtkOutput->SetWorldGeometry(plane);
slicerWithVtkOutput->SetVtkOutputRequest(true);
slicerWithVtkOutput->Update();
vtkSmartPointer<vtkImageData> vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput();
double PixelvalueByVtkOutput = vtkImageByVtkOutput->GetScalarComponentAsDouble(n1, n2, 0, 0);
MITK_TEST_CONDITION(PixelvalueByMitkOutput == PixelvalueByVtkOutput,
"testing conversion of image output vtk->mitk by reslicer");
/*================ log outputs ===========================*/
#ifdef EXTRACTOR_DEBUG
MITK_INFO << "\n"
<< "TESTINFO index: " << sliceindex << " orientation: " << orientation << " frontside: " << isFrontside
<< " rotated: " << isRotated;
MITK_INFO << "\n"
<< "slice index to world: " << sliceIndexToWorld;
MITK_INFO << "\n"
<< "image index to world: " << imageIndexToWorld;
MITK_INFO << "\n"
<< "vtk: slice: " << PixelvalueByMitkOutput << ", image: " << valueVTKinImage;
MITK_INFO << "\n"
<< "testPoint3D InWorld" << testPoint3DInWorld << " is " << testPoint2DInIndex << " in 2D";
MITK_INFO << "\n"
<< "randoms: " << ((int)n1) << ", " << ((int)n2);
MITK_INFO << "\n"
<< "point is inside plane: " << plane->IsInside(testPoint3DInWorld)
<< " and volume: " << imageInMitk->GetGeometry()->IsInside(testPoint3DInWorld);
MITK_INFO << "\n"
<< "volume idx: " << testPoint3DInIndex << " = " << valueAt3DVolume;
MITK_INFO << "\n"
<< "volume world: " << testPoint3DInWorld << " = " << valueAt3DVolumeByWorld;
MITK_INFO << "\n"
<< "slice idx: " << testPoint2DInIndex << " = " << valueAtSlice;
itk::Index<3> curr;
curr[0] = curr[1] = curr[2] = 0;
for (int i = 0; i < 32; ++i)
{
for (int j = 0; j < 32; ++j)
{
++curr[1];
if (SliceReadAccessor.GetPixelByIndex(curr) == valueAt3DVolume)
{
MITK_INFO << "\n" << valueAt3DVolume << " MATCHED mitk " << curr;
}
}
curr[1] = 0;
++curr[0];
}
typedef itk::Image<unsigned short, 2> Image2DType;
Image2DType::Pointer img = Image2DType::New();
CastToItkImage(slice, img);
typedef itk::ImageRegionConstIterator<Image2DType> Iterator2D;
Iterator2D iter(img, img->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
if (img->GetPixel(iter.GetIndex()) == valueAt3DVolume)
MITK_INFO << "\n" << valueAt3DVolume << " MATCHED itk " << iter.GetIndex();
++iter;
}
#endif // EXTRACTOR_DEBUG
}
/* random a float value */
static float randFloat()
{
return (((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) +
(((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) / ((float)RAND_MAX + 1.0);
}
/* create a sphere with the size of the given testVolumeSize*/
static void InitializeTestVolume()
{
#ifdef CREATE_VOLUME
// do sphere creation
ItkVolumeGeneration();
#ifdef SAVE_VOLUME
// save in file
mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New();
writer->SetInput(TestVolume);
std::string file;
std::ostringstream filename;
filename << "C:\\home\\schroedt\\MITK\\Modules\\ImageExtraction\\Testing\\Data\\sphere_";
filename << TestvolumeSize;
filename << ".nrrd";
file = filename.str();
writer->SetFileName(file);
writer->Update();
#endif // SAVE_VOLUME
#endif
#ifndef CREATE_VOLUME // read from file
mitk::StandardFileLocations::Pointer locator = mitk::StandardFileLocations::GetInstance();
std::string filename = locator->FindFile("sphere_512.nrrd.mhd", "Modules/ImageExtraction/Testing/Data");
TestVolume = mitk::IOUtil::Load<mitk::Image>(filename);
#endif
#ifdef CALC_TESTFAILURE_DEVIATION
// get the TestFailureDeviation in %
AccessFixedDimensionByItk(TestVolume, CalcTestFailureDeviation, 3);
#endif
}
// the test result of the sphere reslice
struct SliceProperties
{
double planeDistanceToSphereCenter;
double diameterInMM;
double diameterInPixel;
double diameterCalculated;
double percentageRadiusToPixel;
double areaCalculated;
double areaInPixel;
double percentageAreaCalcToPixel;
};
static mitk::Image::Pointer TestVolume;
static double TestvolumeSize;
static mitk::PlaneGeometry::Pointer TestPlane;
static std::string TestName;
static unsigned char pixelValueSet;
static SliceProperties testResults;
static double TestFailureDeviation;
private:
/*
* Generate a sphere with a radius of TestvolumeSize / 4.0
*/
static void ItkVolumeGeneration()
{
typedef itk::Image<unsigned char, 3> TestVolumeType;
typedef itk::ImageRegionConstIterator<TestVolumeType> ImageIterator;
TestVolumeType::Pointer sphereImage = TestVolumeType::New();
TestVolumeType::IndexType start;
start[0] = start[1] = start[2] = 0;
TestVolumeType::SizeType size;
size[0] = size[1] = size[2] = TestvolumeSize;
TestVolumeType::RegionType imgRegion;
imgRegion.SetSize(size);
imgRegion.SetIndex(start);
sphereImage->SetRegions(imgRegion);
sphereImage->SetSpacing(mitk::Vector(1.0));
sphereImage->Allocate();
sphereImage->FillBuffer(0);
mitk::Vector3D center;
center[0] = center[1] = center[2] = TestvolumeSize / 2.0;
double radius = TestvolumeSize / 4.0;
double pixelValue = pixelValueSet;
ImageIterator imageIterator(sphereImage, sphereImage->GetLargestPossibleRegion());
imageIterator.GoToBegin();
mitk::Vector3D currentVoxelInIndex;
while (!imageIterator.IsAtEnd())
{
currentVoxelInIndex[0] = imageIterator.GetIndex()[0];
currentVoxelInIndex[1] = imageIterator.GetIndex()[1];
currentVoxelInIndex[2] = imageIterator.GetIndex()[2];
double distanceToCenter = (center + (currentVoxelInIndex * -1.0)).GetNorm();
// if distance to center is smaller then the radius of the sphere
if (distanceToCenter < radius)
{
sphereImage->SetPixel(imageIterator.GetIndex(), pixelValue);
}
++imageIterator;
}
CastToMitkImage(sphereImage, TestVolume);
}
/* calculate the deviation of the voxel object to the mathematical sphere object.
* this is use to make a statement about the accuracy of the resliced image, eg. the circle's diameter or area.
*/
template <typename TPixel, unsigned int VImageDimension>
static void CalcTestFailureDeviation(itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::ImageRegionConstIterator<InputImageType> ImageIterator;
ImageIterator iterator(inputImage, inputImage->GetLargestPossibleRegion());
iterator.GoToBegin();
int volumeInPixel = 0;
while (!iterator.IsAtEnd())
{
if (inputImage->GetPixel(iterator.GetIndex()) == pixelValueSet)
volumeInPixel++;
++iterator;
}
double diameter = TestvolumeSize / 2.0;
double volumeCalculated = (1.0 / 6.0) * 3.14159265358979 * std::pow(diameter, 3);
double volumeDeviation = std::abs(100 - (100 / volumeCalculated * volumeInPixel));
typename InputImageType::IndexType index;
index[0] = index[1] = TestvolumeSize / 2.0;
index[2] = 0;
int sumpixels = 0;
while (index[2] < TestvolumeSize)
{
if (inputImage->GetPixel(index) == pixelValueSet)
sumpixels++;
index[2] += 1;
}
double diameterDeviation = std::abs(100 - (100 / diameter * sumpixels));
#ifdef DEBUG
MITK_INFO << "volume deviation: " << volumeDeviation << " diameter deviation:" << diameterDeviation;
#endif
mitkExtractSliceFilterTestClass::TestFailureDeviation = (volumeDeviation + diameterDeviation) / 2.0;
}
};
/*================ #END class ================*/
/*================#BEGIN Instantiation of members ================*/
mitk::Image::Pointer mitkExtractSliceFilterTestClass::TestVolume = mitk::Image::New();
double mitkExtractSliceFilterTestClass::TestvolumeSize = 256.0;
mitk::PlaneGeometry::Pointer mitkExtractSliceFilterTestClass::TestPlane = mitk::PlaneGeometry::New();
std::string mitkExtractSliceFilterTestClass::TestName = "";
unsigned char mitkExtractSliceFilterTestClass::pixelValueSet = 255;
mitkExtractSliceFilterTestClass::SliceProperties mitkExtractSliceFilterTestClass::testResults = {
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0};
double mitkExtractSliceFilterTestClass::TestFailureDeviation = 0.0;
/*================ #END Instantiation of members ================*/
/*================ #BEGIN test main ================*/
int mitkExtractSliceFilterTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkExtractSliceFilterTest")
// pixelvalue based testing
mitkExtractSliceFilterTestClass::PixelvalueBasedTest();
// initialize sphere test volume
mitkExtractSliceFilterTestClass::InitializeTestVolume();
mitk::Vector3D spacing = mitkExtractSliceFilterTestClass::TestVolume->GetGeometry()->GetSpacing();
// the center of the sphere = center of image
double sphereCenter = mitkExtractSliceFilterTestClass::TestvolumeSize / 2.0;
double planeSize = mitkExtractSliceFilterTestClass::TestvolumeSize;
/* axial plane */
mitk::PlaneGeometry::Pointer geometryAxial = mitk::PlaneGeometry::New();
geometryAxial->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Axial, sphereCenter, false, true);
geometryAxial->ChangeImageGeometryConsideringOriginOffset(true);
mitk::Point3D origin = geometryAxial->GetOrigin();
mitk::Vector3D normal;
normal = geometryAxial->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometryAxial->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometryAxial, "Testing axial plane");
/* end axial plane */
/* sagittal plane */
mitk::PlaneGeometry::Pointer geometrySagittal = mitk::PlaneGeometry::New();
geometrySagittal->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Sagittal, sphereCenter, true, false);
geometrySagittal->ChangeImageGeometryConsideringOriginOffset(true);
origin = geometrySagittal->GetOrigin();
normal = geometrySagittal->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometrySagittal->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometrySagittal, "Testing sagittal plane");
/* sagittal plane */
/* sagittal shifted plane */
mitk::PlaneGeometry::Pointer geometrySagittalShifted = mitk::PlaneGeometry::New();
geometrySagittalShifted->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Sagittal, (sphereCenter - 14), true, false);
geometrySagittalShifted->ChangeImageGeometryConsideringOriginOffset(true);
origin = geometrySagittalShifted->GetOrigin();
normal = geometrySagittalShifted->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometrySagittalShifted->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometrySagittalShifted, "Testing sagittal plane shifted");
/* end sagittal shifted plane */
/* coronal plane */
mitk::PlaneGeometry::Pointer geometryCoronal = mitk::PlaneGeometry::New();
geometryCoronal->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Coronal, sphereCenter, true, false);
geometryCoronal->ChangeImageGeometryConsideringOriginOffset(true);
origin = geometryCoronal->GetOrigin();
normal = geometryCoronal->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometryCoronal->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometryCoronal, "Testing coronal plane");
/* end coronal plane */
/* oblique plane */
mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New();
obliquePlane->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Sagittal, sphereCenter, true, false);
obliquePlane->ChangeImageGeometryConsideringOriginOffset(true);
origin = obliquePlane->GetOrigin();
normal = obliquePlane->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// obliquePlane->SetOrigin(origin);
mitk::Vector3D rotationVector;
rotationVector[0] = 0.2;
rotationVector[1] = 0.4;
rotationVector[2] = 0.62;
float degree = 37.0;
mitk::RotationOperation *op =
new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree);
obliquePlane->ExecuteOperation(op);
delete op;
mitkExtractSliceFilterTestClass::TestSlice(obliquePlane, "Testing oblique plane");
/* end oblique plane */
#ifdef SHOW_SLICE_IN_RENDER_WINDOW
/*================ #BEGIN vtk render code ================*/
// set reslicer for renderwindow
mitk::Image::Pointer pic = mitk::IOUtil::Load<mitk::Image>(filename);
vtkSmartPointer<vtkImageReslice> slicer = vtkSmartPointer<vtkImageReslice>::New();
slicer->SetInput(pic->GetVtkImageData());
mitk::PlaneGeometry::Pointer obliquePl = mitk::PlaneGeometry::New();
obliquePl->InitializeStandardPlane(
pic->GetGeometry(), mitk::AnatomicalPlane::Sagittal, pic->GetGeometry()->GetCenter()[0], true, false);
obliquePl->ChangeImageGeometryConsideringOriginOffset(true);
mitk::Point3D origin2 = obliquePl->GetOrigin();
mitk::Vector3D n;
n = obliquePl->GetNormal();
n.Normalize();
origin2 += n * 0.5; // pixelspacing is 1, so half the spacing is 0.5
obliquePl->SetOrigin(origin2);
mitk::Vector3D rotation;
rotation[0] = 0.534307;
rotation[1] = 0.000439605;
rotation[2] = 0.423017;
MITK_INFO << rotation;
mitk::RotationOperation *operation =
new mitk::RotationOperation(mitk::OpROTATE, obliquePl->GetCenter(), rotationVector, degree);
obliquePl->ExecuteOperation(operation);
delete operation;
double origin[3];
origin[0] = obliquePl->GetOrigin()[0];
origin[1] = obliquePl->GetOrigin()[1];
origin[2] = obliquePl->GetOrigin()[2];
slicer->SetResliceAxesOrigin(origin);
mitk::Vector3D right, bottom, normal;
right = obliquePl->GetAxisVector(0);
bottom = obliquePl->GetAxisVector(1);
normal = obliquePl->GetNormal();
right.Normalize();
bottom.Normalize();
normal.Normalize();
double cosines[9];
mitk::vnl2vtk(right.GetVnlVector(), cosines); // x
mitk::vnl2vtk(bottom.GetVnlVector(), cosines + 3); // y
mitk::vnl2vtk(normal.GetVnlVector(), cosines + 6); // n
slicer->SetResliceAxesDirectionCosines(cosines);
slicer->SetOutputDimensionality(2);
slicer->Update();
// set vtk renderwindow
vtkSmartPointer<vtkPlaneSource> vtkPlane = vtkSmartPointer<vtkPlaneSource>::New();
vtkPlane->SetOrigin(0.0, 0.0, 0.0);
// 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 sagittal) afterwards.
vtkPlane->SetPoint1(1.0, 0.0, 0.0); // P1: (xMax, yMin, depth)
vtkPlane->SetPoint2(0.0, 1.0, 0.0); // P2: (xMin, yMax, depth)
// these are not the correct values for all slices, only a square plane by now
vtkSmartPointer<vtkPolyDataMapper> imageMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
imageMapper->SetInputConnection(vtkPlane->GetOutputPort());
vtkSmartPointer<vtkLookupTable> lookupTable = vtkSmartPointer<vtkLookupTable>::New();
// built a default lookuptable
lookupTable->SetRampToLinear();
lookupTable->SetSaturationRange(0.0, 0.0);
lookupTable->SetHueRange(0.0, 0.0);
lookupTable->SetValueRange(0.0, 1.0);
lookupTable->Build();
// map all black values to transparent
lookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0);
lookupTable->SetRange(-255.0, 255.0);
// lookupTable->SetRange(-1022.0, 1184.0);//pic3D range
vtkSmartPointer<vtkTexture> texture = vtkSmartPointer<vtkTexture>::New();
texture->SetInput(slicer->GetOutput());
texture->SetLookupTable(lookupTable);
texture->SetMapColorScalarsThroughLookupTable(true);
vtkSmartPointer<vtkActor> imageActor = vtkSmartPointer<vtkActor>::New();
imageActor->SetMapper(imageMapper);
imageActor->SetTexture(texture);
// Setup renderers
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(imageActor);
// Setup render window
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
// Setup render window interactor
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
// Render and start interaction
renderWindowInteractor->SetRenderWindow(renderWindow);
// renderer->AddViewProp(imageActor);
renderWindow->Render();
renderWindowInteractor->Start();
// always end with this!
/*================ #END vtk render code ================*/
#endif // SHOW_SLICE_IN_RENDER_WINDOW
MITK_TEST_END()
}
diff --git a/Modules/Core/test/mitkFileReaderRegistryTest.cpp b/Modules/Core/test/mitkFileReaderRegistryTest.cpp
index 94b414c0fc..c42917bf48 100644
--- a/Modules/Core/test/mitkFileReaderRegistryTest.cpp
+++ b/Modules/Core/test/mitkFileReaderRegistryTest.cpp
@@ -1,212 +1,212 @@
/*============================================================================
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 "mitkAbstractFileReader.h"
#include "mitkFileReaderRegistry.h"
#include "mitkIFileReader.h"
#include "mitkTestingMacros.h"
#include <mitkBaseData.h>
#include <mitkCustomMimeType.h>
#include <mitkImage.h>
class DummyReader : public mitk::AbstractFileReader
{
public:
DummyReader(const DummyReader &other) : mitk::AbstractFileReader(other) {}
DummyReader(const std::string &mimeTypeName, const std::string &extension, int priority) : mitk::AbstractFileReader()
{
mitk::CustomMimeType mimeType(mimeTypeName);
mimeType.AddExtension(extension);
mimeType.SetComment("This is a dummy description");
this->SetMimeType(mimeType);
this->SetRanking(priority);
m_ServiceReg = this->RegisterService();
}
~DummyReader() override
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using mitk::AbstractFileReader::Read;
std::vector<itk::SmartPointer<mitk::BaseData>> DoRead() override
{
std::vector<mitk::BaseData::Pointer> result;
return result;
}
private:
DummyReader *Clone() const override { return new DummyReader(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy reader
class DummyReader2 : public mitk::AbstractFileReader
{
public:
DummyReader2(const DummyReader2 &other) : mitk::AbstractFileReader(other) {}
DummyReader2(const std::string &mimeTypeName, const std::string &extension, int priority) : mitk::AbstractFileReader()
{
mitk::CustomMimeType mimeType(mimeTypeName);
mimeType.AddExtension(extension);
mimeType.SetComment("This is a second dummy description");
this->SetMimeType(mimeType);
this->SetRanking(priority);
m_ServiceReg = this->RegisterService();
}
~DummyReader2() override
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using mitk::AbstractFileReader::Read;
std::vector<itk::SmartPointer<mitk::BaseData>> DoRead() override
{
std::vector<mitk::BaseData::Pointer> result;
return result;
}
private:
DummyReader2 *Clone() const override { return new DummyReader2(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy reader 2
/**
* TODO
*/
int mitkFileReaderRegistryTest(int /*argc*/, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("FileReaderRegistry");
// mitk::FileReaderRegistry::Pointer frm = mitk::FileReaderRegistry::New();
// MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing FileReaderRegistry instantiation");
// DummyReader testDR("application/dummy", "test",1);
// DummyReader otherDR("application/dummy2", "other",1);
// MITK_TEST_CONDITION_REQUIRED(!testDR.CanRead("/this/is/a/folder/file.tes"),"Negative test of default CanRead()
// implementation");
// mitk::FileReaderRegistry* readerRegistry = new mitk::FileReaderRegistry;
// mitk::IFileReader* returned = readerRegistry->GetReader("bla.test");
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(testDR) != returned,"Testing correct
// retrieval of FileReader 1/2");
// returned = readerRegistry->GetReader("other");
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(otherDR) != returned,"Testing correct
// retrieval of FileReader 2/2");
// DummyReader mediocreTestDR("application/dummy", "test", 20);
// DummyReader prettyFlyTestDR("application/dummy", "test", 50);
// DummyReader2 awesomeTestDR("application/dummy", "test", 100);
// returned = readerRegistry->GetReader("test");
- // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returned), "Testing correct priorized retrieval of
+ // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returned), "Testing correct prioritized retrieval of
// FileReader: Best reader");
// Now to give those readers some options, then we will try again
// mitk::IFileReader::OptionList options;
// options.push_back(std::make_pair("isANiceGuy", true));
// mediocreTestDR.SetOptions(options);
// options.clear();
// options.push_back(std::make_pair("canFly", true));
// prettyFlyTestDR.SetOptions(options);
// options.push_back(std::make_pair("isAwesome", true));
// awesomeTestDR.SetOptions(options); //note: awesomeReader canFly and isAwesome
// // Reset Options, use to define what we want the reader to do
// options.clear();
// mitk::IFileReader::OptionNames optionsFilter;
// optionsFilter.push_back("canFly");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(awesomeTestDR) != returned, "Testing
// correct retrieval of FileReader with Options: Best reader with options");
// optionsFilter.push_back("isAwesome");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(awesomeTestDR) != returned, "Testing
// correct retrieval of FileReader with multiple Options: Best reader with options");
// optionsFilter.clear();
// optionsFilter.push_back("isANiceGuy");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(mediocreTestDR) != returned, "Testing
// correct retrieval of specific FileReader with Options: Low priority reader with specific option");
// optionsFilter.push_back("canFly");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned == nullptr, "Testing correct return of 0 value when no matching reader was
// found");
// // Onward to test the retrieval of multiple readers
// std::vector< mitk::IFileReader* > returnedList;
// returnedList = readerRegistry->GetReaders("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returnedList.empty(), "Testing correct return of zero readers when no matching reader
// was found, asking for all compatibles");
// optionsFilter.clear();
// optionsFilter.push_back("canFly");
// returnedList = readerRegistry->GetReaders("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 2, "Testing correct return of two readers when two matching
// reader was found, asking for all compatibles");
- // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returnedList.front()), "Testing correct priorization of
+ // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returnedList.front()), "Testing correct prioritization of
// returned Readers with options 1/2");
// optionsFilter.clear();
// optionsFilter.push_back("isAwesome");
// returnedList = readerRegistry->GetReaders("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 1, "Testing correct return of one readers when one matching
// reader was found, asking for all compatibles");
// MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returnedList.front()), "Testing correctness of result
// from former query");
// And now to verify a working read chain for a mps file:
// mitk::PointSetReader::Pointer psr = mitk::PointSetReader::New();
// std::vector<mitk::BaseData::Pointer> basedata;
// basedata = mitk::FileReaderRegistry::Read("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(basedata.size() > 0, "Testing correct read of PointSet");
// Testing templated call to ReaderRegistry
// mitk::PointSet::Pointer pointset = mitk::FileReaderRegistry::Read< mitk::PointSet
// >("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(pointset.IsNotNull(), "Testing templated call of Read()");
// And now for something completely different... (Debug)
// mitk::LegacyFileReaderService::Pointer lfr = mitk::LegacyFileReaderService::New(".nrrd", "Nearly Raw Raster Data");
// returned = mitk::FileReaderRegistry::GetReader(".nrrd");
// MITK_TEST_CONDITION_REQUIRED(lfr == returned, "Testing correct retrieval of specific FileReader with Options: Low
// priority reader with specific option");
// std::vector<mitk::BaseData::Pointer> image =
// mitk::FileReaderRegistry::Read("F://Build//MITK-Data//Pic2DplusT.nrrd");
// MITK_TEST_CONDITION_REQUIRED(image.size() > 0, "Testing whether image was returned or not");
// mitk::Image::Pointer image2 = dynamic_cast<mitk::Image*> (image.front().GetPointer());
// MITK_TEST_CONDITION_REQUIRED(image2.IsNotNull(), "Testing if BaseData is an image");
// Delete this here because it will call the PrototypeServiceFactory::Unget() method
// of the dummy readers.
// delete readerRegistry;
// always end with this!
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkFileWriterRegistryTest.cpp b/Modules/Core/test/mitkFileWriterRegistryTest.cpp
index cf811c6487..cc0ca54639 100644
--- a/Modules/Core/test/mitkFileWriterRegistryTest.cpp
+++ b/Modules/Core/test/mitkFileWriterRegistryTest.cpp
@@ -1,288 +1,288 @@
/*============================================================================
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 <mitkAbstractFileWriter.h>
#include <mitkBaseData.h>
#include <mitkFileReaderRegistry.h>
#include <mitkFileWriterRegistry.h>
#include <mitkIFileWriter.h>
#include <mitkIOUtil.h>
#include <mitkTestingMacros.h>
class DummyBaseData : public mitk::BaseData
{
public:
mitkClassMacro(DummyBaseData, mitk::BaseData) itkNewMacro(Self)
void SetRequestedRegion(const itk::DataObject * /*data*/)
{
}
void SetRequestedRegionToLargestPossibleRegion() {}
bool RequestedRegionIsOutsideOfTheBufferedRegion() { return false; }
bool VerifyRequestedRegion() { return true; }
};
class DummyWriter : public mitk::AbstractFileWriter
{
public:
DummyWriter(const DummyWriter &other) : mitk::AbstractFileWriter(other), m_Content("Hi there stream") {}
DummyWriter(const std::string &basedataType, const std::string &extension, int ranking)
: mitk::AbstractFileWriter(basedataType, extension, "This is a dummy description"), m_Content("Hi there stream")
{
this->SetRanking(ranking);
m_ServiceReg = this->RegisterService();
}
~DummyWriter()
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using AbstractFileWriter::Write;
virtual void Write(const mitk::BaseData *data, std::ostream &stream)
{
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<const DummyBaseData *>(data), "Correct data type")
stream << m_Content;
}
std::string m_Content;
private:
DummyWriter *Clone() const { return new DummyWriter(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy Writer
class DummyWriter2 : public mitk::AbstractFileWriter
{
public:
DummyWriter2(const DummyWriter2 &other) : mitk::AbstractFileWriter(other), m_Content("hi there file path") {}
DummyWriter2(const std::string &basedataType, const std::string &extension, int ranking)
: mitk::AbstractFileWriter(basedataType, extension, "This is a dummy description"), m_Content("hi there file path")
{
this->SetRanking(ranking);
m_ServiceReg = this->RegisterService();
}
~DummyWriter2()
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using AbstractFileWriter::Write;
virtual void Write(const mitk::BaseData *data, const std::string &filePath)
{
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<const DummyBaseData *>(data), "Correct data type")
std::ofstream fileStream(filePath.c_str());
fileStream << m_Content;
}
virtual void Write(const mitk::BaseData *data, std::ostream &stream)
{
mitk::AbstractFileWriter::Write(data, stream);
}
virtual bool CanWrite(const mitk::BaseData *data) const { return dynamic_cast<const DummyBaseData *>(data); }
std::string m_Content;
private:
DummyWriter2 *Clone() const { return new DummyWriter2(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy Writer 2
void TestStreamMethods()
{
DummyWriter dummyWriter(DummyBaseData::GetStaticNameOfClass(), "stream", 100);
DummyWriter2 dummyWriter2(DummyBaseData::GetStaticNameOfClass(), "file", 50);
mitk::FileWriterRegistry writerRegistry;
// Test DummyWriter, which always uses a ostream for writing, even
// when a file path is used
DummyBaseData dummyData;
std::stringstream oss;
writerRegistry.Write(&dummyData, oss);
MITK_TEST_CONDITION_REQUIRED(dummyWriter.m_Content == oss.str(), "Dummy stream writer")
std::string content;
{
std::ofstream tmpStream;
std::string tmpFileName = mitk::IOUtil::CreateTemporaryFile(tmpStream);
writerRegistry.Write(&dummyData, tmpFileName);
std::ifstream tmpInput(tmpFileName.c_str());
std::getline(tmpInput, content);
tmpInput.close();
tmpStream.close();
std::remove(tmpFileName.c_str());
}
MITK_TEST_CONDITION_REQUIRED(dummyWriter.m_Content == content, "Dummy stream writer")
// Test DummyWriter2, which always uses a real file for writing, even
// when a std::ostream object is given
std::stringstream oss2;
dummyWriter2.Write(&dummyData, oss2);
MITK_TEST_CONDITION_REQUIRED(dummyWriter2.m_Content == oss2.str(), "Dummy 2 stream writer")
std::string content2;
{
std::ofstream tmpStream;
std::string tmpFileName = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.file");
writerRegistry.Write(&dummyData, tmpFileName);
std::ifstream tmpInput(tmpFileName.c_str());
std::getline(tmpInput, content2);
tmpInput.close();
std::remove(tmpFileName.c_str());
}
MITK_TEST_CONDITION_REQUIRED(dummyWriter2.m_Content == content2, "Dummy 2 stream writer")
}
/**
* TODO
*/
int mitkFileWriterRegistryTest(int /*argc*/, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("FileWriterRegistry");
TestStreamMethods();
// mitk::FileWriterRegistry::Pointer frm = mitk::FileWriterRegistry::New();
// MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing FileWriterRegistry instantiation");
DummyWriter testDR("testdata", "test", 1);
DummyWriter otherDR("testdata", "other", 1);
// MITK_TEST_CONDITION_REQUIRED(testDR->CanWrite("/this/is/a/folder/file.test"),"Positive test of default CanRead()
// implementation");
// MITK_TEST_CONDITION_REQUIRED(!testDR->CanWrite("/this/is/a/folder/file.tes"),"Negative test of default CanRead()
// implementation");
mitk::FileWriterRegistry *writerRegistry = new mitk::FileWriterRegistry;
mitk::IFileWriter *returned = writerRegistry->GetWriter("", "test");
MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileWriter &>(testDR) != returned,
"Testing correct retrieval of FileWriter 1/2");
returned = writerRegistry->GetWriter("", "other");
MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileWriter &>(otherDR) != returned,
"Testing correct retrieval of FileWriter 2/2");
DummyWriter mediocreTestDR("testdata", "test", 20);
DummyWriter prettyFlyTestDR("testdata", "test", 50);
DummyWriter2 awesomeTestDR("testdata", "test", 100);
returned = writerRegistry->GetWriter("test");
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyWriter2 *>(returned),
- "Testing correct priorized retrieval of FileWriter: Best Writer");
+ "Testing correct prioritized retrieval of FileWriter: Best Writer");
// Now to give those Writers some options, then we will try again
mitk::IFileWriter::OptionList options;
options.push_back(std::make_pair("isANiceGuy", true));
mediocreTestDR.SetOptions(options);
options.clear();
options.push_back(std::make_pair("canFly", true));
prettyFlyTestDR.SetOptions(options);
options.push_back(std::make_pair("isAwesome", true));
awesomeTestDR.SetOptions(options); // note: awesomeWriter canFly and isAwesome
// Reset Options, use to define what we want the Writer to do
mitk::IFileWriter::OptionNames optionFilter;
optionFilter.push_back("canFly");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileWriter &>(awesomeTestDR) != returned,
"Testing correct retrieval of FileWriter with Options: Best Writer with options");
optionFilter.push_back("isAwesome");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returned && &static_cast<mitk::IFileWriter &>(awesomeTestDR) != returned,
"Testing correct retrieval of FileWriter with multiple Options: Best Writer with options");
optionFilter.clear();
optionFilter.push_back("isANiceGuy");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returned && &static_cast<mitk::IFileWriter &>(mediocreTestDR) != returned,
"Testing correct retrieval of specific FileWriter with Options: Low priority Writer with specific option");
optionFilter.push_back("canFly");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(returned == nullptr, "Testing correct return of 0 value when no matching Writer was found");
// Onward to test the retrieval of multiple Writers
std::vector<mitk::IFileWriter *> returnedList;
returnedList = writerRegistry->GetWriters("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returnedList.empty(),
"Testing correct return of zero Writers when no matching Writer was found, asking for all compatibles");
optionFilter.clear();
optionFilter.push_back("canFly");
returnedList = writerRegistry->GetWriters("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returnedList.size() == 2,
"Testing correct return of two Writers when two matching Writer was found, asking for all compatibles");
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyWriter2 *>(returnedList.front()),
- "Testing correct priorization of returned Writers with options 1/2");
+ "Testing correct prioritization of returned Writers with options 1/2");
optionFilter.clear();
optionFilter.push_back("isAwesome");
returnedList = writerRegistry->GetWriters("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returnedList.size() == 1,
"Testing correct return of one Writers when one matching Writer was found, asking for all compatibles");
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyWriter2 *>(returnedList.front()),
"Testing correctness of result from former query");
// mitk::CoreObjectFactory::GetInstance();
// mitk::FileReaderRegistry readerRegistry;
// mitk::Image::Pointer image = readerRegistry.Read<mitk::Image>("F://Build//MITK-Data//Pic2DplusT.nrrd");
// writerRegistry->Write(image.GetPointer(), "F://Build//MITK-Data//Pic2DplusTcopy.nrrd");
//// And now to verify a working read chain for a mps file:
// mitk::PointSetWriter::Pointer psr = mitk::PointSetWriter::New();
// mitk::BaseData::Pointer basedata;
// basedata = mitk::FileWriterRegistry::Read("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(basedata.IsNotNull(), "Testing correct read of PointSet");
//// Testing templated call to WriterRegistry
// mitk::PointSet::Pointer pointset = mitk::FileWriterRegistry::Read< mitk::PointSet
// >("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(pointset.IsNotNull(), "Testing templated call of Read()");
//// And now for something completely different... (Debug)
// mitk::LegacyFileWriterService::Pointer lfr = mitk::LegacyFileWriterService::New(".nrrd", "Nearly Raw Raster Data");
// returned = mitk::FileWriterRegistry::GetWriter(".nrrd");
// MITK_TEST_CONDITION_REQUIRED(lfr == returned, "Testing correct retrieval of specific FileWriter with Options: Low
// priority Writer with specific option");
// mitk::BaseData::Pointer image = mitk::FileWriterRegistry::Read("F://Build//MITK-Data//Pic2DplusT.nrrd");
// MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Testing whether BaseData is empty or not");
// mitk::Image::Pointer image2 = dynamic_cast<mitk::Image*> (image.GetPointer());
// MITK_TEST_CONDITION_REQUIRED(image2.IsNotNull(), "Testing if BaseData is an image");
// Delete this here because it will call the PrototypeServiceFactory::Unget() method
// of the dummy writers.
delete writerRegistry;
//// always end with this!
MITK_TEST_END()
}
diff --git a/Modules/Core/test/mitkImageDimensionConverterTest.cpp b/Modules/Core/test/mitkImageDimensionConverterTest.cpp
index 2428741336..7216be83e4 100644
--- a/Modules/Core/test/mitkImageDimensionConverterTest.cpp
+++ b/Modules/Core/test/mitkImageDimensionConverterTest.cpp
@@ -1,260 +1,260 @@
/*============================================================================
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 "mitkTestingConfig.h"
#include <mitkConvert2Dto3DImageFilter.h>
#include <mitkIOUtil.h>
#include <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkImageDataItem.h>
#include <mitkImageStatisticsHolder.h>
#include <mitkInteractionConst.h>
#include <mitkPlaneOperation.h>
#include <mitkRotationOperation.h>
#include <mitkTestingMacros.h>
// itk includes
#include <itkImage.h>
#include <itkMersenneTwisterRandomVariateGenerator.h>
// stl includes
#include <fstream>
// vtk includes
#include <vtkImageData.h>
int mitkImageDimensionConverterTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN(mitkImageDimensionConverterTest);
// Define an epsilon which is the allowed error
float eps = 0.00001;
// Define helper variables
float error = 0;
bool matrixIsEqual = true;
std::stringstream sstream;
mitk::Convert2Dto3DImageFilter::Pointer convertFilter = mitk::Convert2Dto3DImageFilter::New();
///////////////////////////////////////
// Create 2D Image with a 3D rotation from scratch.
typedef itk::Image<double, 2> ImageType;
ImageType::Pointer itkImage = ImageType::New();
ImageType::RegionType myRegion;
ImageType::SizeType mySize;
ImageType::IndexType myIndex;
ImageType::SpacingType mySpacing;
mySpacing[0] = 1;
mySpacing[1] = 1;
myIndex[0] = 0;
myIndex[1] = 0;
mySize[0] = 50;
mySize[1] = 50;
myRegion.SetSize(mySize);
myRegion.SetIndex(myIndex);
itkImage->SetSpacing(mySpacing);
itkImage->SetRegions(myRegion);
itkImage->Allocate();
itkImage->FillBuffer(50);
mitk::Image::Pointer mitkImage2D;
mitk::CastToMitkImage(itkImage, mitkImage2D);
// rotate
mitk::Point3D p;
p[0] = 1;
p[1] = 3;
p[2] = 5;
mitk::Vector3D v;
v[0] = 0.3;
v[1] = 1;
v[2] = 0.1;
mitk::RotationOperation op(mitk::OpROTATE, p, v, 35);
mitkImage2D->GetGeometry()->ExecuteOperation(&op);
// Save original Geometry infos
mitk::Vector3D Original_col0, Original_col1, Original_col2;
Original_col0.SetVnlVector(
mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Original_col1.SetVnlVector(
mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Original_col2.SetVnlVector(
mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
MITK_INFO << "Rotated Matrix: " << Original_col0 << " " << Original_col1 << " " << Original_col2;
mitk::Point3D Original_Origin = mitkImage2D->GetGeometry()->GetOrigin();
mitk::Vector3D Original_Spacing = mitkImage2D->GetGeometry()->GetSpacing();
MITK_TEST_CONDITION_REQUIRED(mitkImage2D->GetDimension() == 2, "Created Image is Dimension 2");
///////////////////////////////////////
// mitkImage2D is now a 2D image with 3D Geometry information.
- // Save it without conversion and load it again. It should have an identitiy matrix
+ // Save it without conversion and load it again. It should have an identity matrix
sstream.clear();
sstream << MITK_TEST_OUTPUT_DIR << ""
<< "/rotatedImage2D.nrrd";
mitk::IOUtil::Save(mitkImage2D, sstream.str());
mitk::Image::Pointer imageLoaded2D = mitk::IOUtil::Load<mitk::Image>(sstream.str());
// check if image can be loaded
MITK_TEST_CONDITION_REQUIRED(imageLoaded2D.IsNotNull(), "Loading saved 2D Image");
MITK_TEST_CONDITION_REQUIRED(imageLoaded2D->GetDimension() == 2, "Loaded Image is Dimension 2");
// check if spacing is ok
mitk::Vector3D Loaded2D_Spacing = imageLoaded2D->GetGeometry()->GetSpacing();
error = std::fabs(Loaded2D_Spacing[0] - Original_Spacing[0]) + std::fabs(Loaded2D_Spacing[1] - Original_Spacing[1]) +
std::fabs(Loaded2D_Spacing[2] - 1);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing");
// Check origin
mitk::Point3D Loaded2D_Origin = imageLoaded2D->GetGeometry()->GetOrigin();
error = std::fabs(Loaded2D_Origin[0] - Original_Origin[0]) + std::fabs(Loaded2D_Origin[1] - Original_Origin[1]) +
std::fabs(Loaded2D_Origin[2] - 0);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin");
// Check matrix
mitk::Vector3D Loaded2D_col0, Loaded2D_col1, Loaded2D_col2;
Loaded2D_col0.SetVnlVector(
imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Loaded2D_col1.SetVnlVector(
imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Loaded2D_col2.SetVnlVector(
imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((std::fabs(1 - Loaded2D_col0[0]) > eps) || (std::fabs(0 - Loaded2D_col0[1]) > eps) ||
(std::fabs(0 - Loaded2D_col0[2]) > eps) || (std::fabs(0 - Loaded2D_col1[0]) > eps) ||
(std::fabs(1 - Loaded2D_col1[1]) > eps) || (std::fabs(0 - Loaded2D_col1[2]) > eps) ||
(std::fabs(0 - Loaded2D_col2[0]) > eps) || (std::fabs(0 - Loaded2D_col2[1]) > eps) ||
(std::fabs(1 - Loaded2D_col2[2]) > eps))
{
matrixIsEqual = false;
}
else
matrixIsEqual = true;
MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix");
///////////////////////////////////////
// mitkImage2D is a 2D image with 3D Geometry information.
// Convert it with filter to a 3D image and check if everything went well
convertFilter->SetInput(mitkImage2D);
convertFilter->Update();
mitk::Image::Pointer mitkImage3D = convertFilter->GetOutput();
MITK_TEST_CONDITION_REQUIRED(mitkImage3D->GetDimension() == 3, "Converted Image is Dimension 3");
// check if geometry is still same
mitk::Vector3D Converted_Spacing = mitkImage3D->GetGeometry()->GetSpacing();
error = std::fabs(Converted_Spacing[0] - Original_Spacing[0]) +
std::fabs(Converted_Spacing[1] - Original_Spacing[1]) + std::fabs(Converted_Spacing[2] - Original_Spacing[2]);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing");
mitk::Point3D Converted_Origin = mitkImage3D->GetGeometry()->GetOrigin();
error = std::fabs(Converted_Origin[0] - Original_Origin[0]) + std::fabs(Converted_Origin[1] - Original_Origin[1]) +
std::fabs(Converted_Origin[2] - Original_Origin[2]);
MITK_INFO << Converted_Origin << " and " << Original_Origin;
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin");
mitk::Vector3D Converted_col0, Converted_col1, Converted_col2;
Converted_col0.SetVnlVector(
mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Converted_col1.SetVnlVector(
mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Converted_col2.SetVnlVector(
mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((std::fabs(Original_col0[0] - Converted_col0[0]) > eps) ||
(std::fabs(Original_col0[1] - Converted_col0[1]) > eps) ||
(std::fabs(Original_col0[2] - Converted_col0[2]) > eps) ||
(std::fabs(Original_col1[0] - Converted_col1[0]) > eps) ||
(std::fabs(Original_col1[1] - Converted_col1[1]) > eps) ||
(std::fabs(Original_col1[2] - Converted_col1[2]) > eps) ||
(std::fabs(Original_col2[0] - Converted_col2[0]) > eps) ||
(std::fabs(Original_col2[1] - Converted_col2[1]) > eps) ||
(std::fabs(Original_col2[2] - Converted_col2[2]) > eps))
{
MITK_INFO << "Oh No! Original Image Matrix and Converted Image Matrix are different!";
MITK_INFO << "original Image:" << Original_col0 << " " << Original_col1 << " " << Original_col2;
MITK_INFO << "converted Image:" << Converted_col0 << " " << Converted_col1 << " " << Converted_col2;
matrixIsEqual = false;
}
else
matrixIsEqual = true;
MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix");
///////////////////////////////////////
// So far it seems good! now try to save and load the file
std::stringstream sstream2;
sstream2 << MITK_TEST_OUTPUT_DIR << ""
<< "/rotatedImage.nrrd";
mitk::IOUtil::Save(mitkImage3D, sstream2.str());
mitk::Image::Pointer imageLoaded = mitk::IOUtil::Load<mitk::Image>(sstream2.str());
// check if image can be loaded
MITK_TEST_CONDITION_REQUIRED(imageLoaded.IsNotNull(), "Loading saved Image");
// check if loaded image is still what it should be:
MITK_TEST_CONDITION_REQUIRED(imageLoaded->GetDimension() == 3, "Loaded Image is Dimension 3");
// check if geometry is still same
mitk::Vector3D Loaded_Spacing = imageLoaded->GetGeometry()->GetSpacing();
error = std::fabs(Loaded_Spacing[0] - Original_Spacing[0]) + std::fabs(Loaded_Spacing[1] - Original_Spacing[1]) +
std::fabs(Loaded_Spacing[2] - Original_Spacing[2]);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing");
mitk::Point3D Loaded_Origin = imageLoaded->GetGeometry()->GetOrigin();
error = std::fabs(Loaded_Origin[0] - Original_Origin[0]) + std::fabs(Loaded_Origin[1] - Original_Origin[1]) +
std::fabs(Loaded_Origin[2] - Original_Origin[2]);
MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin");
mitk::Vector3D Loaded_col0, Loaded_col1, Loaded_col2;
Loaded_col0.SetVnlVector(
imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref());
Loaded_col1.SetVnlVector(
imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Loaded_col2.SetVnlVector(
imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref());
if ((std::fabs(Original_col0[0] - Loaded_col0[0]) > eps) || (std::fabs(Original_col0[1] - Loaded_col0[1]) > eps) ||
(std::fabs(Original_col0[2] - Loaded_col0[2]) > eps) || (std::fabs(Original_col1[0] - Loaded_col1[0]) > eps) ||
(std::fabs(Original_col1[1] - Loaded_col1[1]) > eps) || (std::fabs(Original_col1[2] - Loaded_col1[2]) > eps) ||
(std::fabs(Original_col2[0] - Loaded_col2[0]) > eps) || (std::fabs(Original_col2[1] - Loaded_col2[1]) > eps) ||
(std::fabs(Original_col2[2] - Loaded_col2[2]) > eps))
{
MITK_INFO << "Oh No! Original Image Matrix and Converted Image Matrix are different!";
MITK_INFO << "original Image:" << Original_col0 << " " << Original_col1 << " " << Original_col2;
MITK_INFO << "converted Image:" << Loaded_col0 << " " << Loaded_col1 << " " << Loaded_col2;
matrixIsEqual = false;
}
else
matrixIsEqual = true;
MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix");
return 0;
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkImageToItkTest.cpp b/Modules/Core/test/mitkImageToItkTest.cpp
index a5633761ac..fc9f221ccd 100644
--- a/Modules/Core/test/mitkImageToItkTest.cpp
+++ b/Modules/Core/test/mitkImageToItkTest.cpp
@@ -1,159 +1,159 @@
/*============================================================================
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 "itkDiffusionTensor3D.h"
#include "mitkITKImageImport.h"
#include "mitkImage.h"
#include "mitkReferenceCountWatcher.h"
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
#include <fstream>
#include <mitkIOUtil.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
static mitk::Image::Pointer GetEmptyTestImageWithGeometry(mitk::PixelType pt)
{
// create empty image
mitk::Image::Pointer imgMem;
imgMem = mitk::Image::New();
// create geometry information for image
mitk::Point3D origin;
mitk::Vector3D right, bottom;
mitk::Vector3D spacing;
mitk::FillVector3D(origin, 17.0, 19.92, 7.83);
mitk::FillVector3D(right, 1.0, 2.0, 3.0);
mitk::FillVector3D(bottom, 0.0, -3.0, 2.0);
mitk::FillVector3D(spacing, 0.78, 0.91, 2.23);
mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New();
planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing);
planegeometry->SetOrigin(origin);
// initialize image
imgMem->Initialize(pt, 40, *planegeometry);
return imgMem;
}
class mitkImageToItkTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageToItkTestSuite);
MITK_TEST(ImageCastIntToFloat_EmptyImage);
MITK_TEST(ImageCastDoubleToFloat_EmptyImage);
MITK_TEST(ImageCastFloatToFloatTensor_EmptyImage_ThrowsException);
MITK_TEST(ImageCastFloatTensorToFloatTensor_EmptyImage);
MITK_TEST(ImageCastDoubleToTensorDouble_EmptyImage_ThrowsException);
MITK_TEST(ImageCastToItkAndBack_SamePointer_Success);
MITK_TEST(ImageCastToItk_TestImage_Success);
CPPUNIT_TEST_SUITE_END();
private:
mitk::Image::Pointer m_TestImage;
public:
void setUp() override
{
// empty on purpose
}
void tearDown() override { m_TestImage = nullptr; }
void ImageCastIntToFloat_EmptyImage()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<int>());
itk::Image<float, 3>::Pointer itkImage;
mitk::CastToItkImage(m_TestImage, itkImage);
mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(itkImage);
CPPUNIT_ASSERT_MESSAGE("Cast output is not null", mitkImageAfterCast.IsNotNull());
}
void ImageCastDoubleToFloat_EmptyImage()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<double>());
itk::Image<float, 3>::Pointer diffImage;
mitk::CastToItkImage(m_TestImage, diffImage);
CPPUNIT_ASSERT_MESSAGE("Casting scalar double (MITK) image to scalar float tensor (ITK). Result shouldn't be nullptr.",
diffImage.IsNotNull());
}
void ImageCastFloatToFloatTensor_EmptyImage_ThrowsException()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<float>());
itk::Image<itk::DiffusionTensor3D<float>, 3>::Pointer diffImage;
CPPUNIT_ASSERT_THROW_MESSAGE("Casting scalar float (MITK) image to scalar float (ITK) throws exception.",
mitk::CastToItkImage(m_TestImage, diffImage),
mitk::AccessByItkException);
}
void ImageCastFloatTensorToFloatTensor_EmptyImage()
{
typedef itk::Image<itk::DiffusionTensor3D<float>, 3> ItkTensorImageType;
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakePixelType<ItkTensorImageType>());
itk::Image<itk::DiffusionTensor3D<float>, 3>::Pointer diffImage;
mitk::CastToItkImage(m_TestImage, diffImage);
MITK_TEST_CONDITION_REQUIRED(diffImage.IsNotNull(),
"Casting float tensor (MITK) image to float tensor (ITK). Result shouldn't be nullptr");
mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(diffImage);
- MITK_ASSERT_EQUAL(mitkImageAfterCast, m_TestImage, "Same type, images shoul be equal.");
+ MITK_ASSERT_EQUAL(mitkImageAfterCast, m_TestImage, "Same type, images should be equal.");
}
void ImageCastDoubleToTensorDouble_EmptyImage_ThrowsException()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<double>());
itk::Image<itk::DiffusionTensor3D<double>, 3>::Pointer diffImage;
CPPUNIT_ASSERT_THROW(mitk::CastToItkImage(m_TestImage, diffImage), mitk::AccessByItkException);
}
void ImageCastToItkAndBack_SamePointer_Success()
{
typedef itk::Image<short, 3> ItkImageType;
ItkImageType::Pointer itkImage = ItkImageType::New();
std::string m_ImagePath = GetTestDataFilePath("Pic3D.nrrd");
mitk::Image::Pointer testDataImage = mitk::IOUtil::Load<mitk::Image>(m_ImagePath);
// modify ITK image
itk::Matrix<double, 3, 3> dir = itkImage->GetDirection();
dir *= 2;
itkImage->SetDirection(dir);
CPPUNIT_ASSERT_THROW_MESSAGE("No exception thrown for casting back the same memory",
testDataImage = mitk::GrabItkImageMemory(itkImage, testDataImage),
itk::ExceptionObject);
CPPUNIT_ASSERT(testDataImage.IsNotNull());
}
void ImageCastToItk_TestImage_Success()
{
itk::Image<short, 3>::Pointer itkImage;
std::string m_ImagePath = GetTestDataFilePath("Pic3D.nrrd");
mitk::Image::Pointer testDataImage = mitk::IOUtil::Load<mitk::Image>(m_ImagePath);
mitk::CastToItkImage(testDataImage, itkImage);
mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(itkImage);
// dereference itk image
itkImage = nullptr;
MITK_ASSERT_EQUAL(mitkImageAfterCast, testDataImage, "Cast with test data followed by import produces same images");
}
}; // END TEST SUITE CLASS DECL
MITK_TEST_SUITE_REGISTRATION(mitkImageToItk);
diff --git a/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake b/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake
index 26f6cc264f..8fca1803eb 100644
--- a/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake
+++ b/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake
@@ -1,288 +1,288 @@
# - CONFIGURE_PACKAGE_CONFIG_FILE(), WRITE_BASIC_PACKAGE_VERSION_FILE()
#
# CONFIGURE_PACKAGE_CONFIG_FILE(<input> <output> INSTALL_DESTINATION <path>
# [PATH_VARS <var1> <var2> ... <varN>]
# [NO_SET_AND_CHECK_MACRO]
# [NO_CHECK_REQUIRED_COMPONENTS_MACRO])
#
# CONFIGURE_PACKAGE_CONFIG_FILE() should be used instead of the plain
# configure_file() command when creating the <Name>Config.cmake or <Name>-config.cmake
# file for installing a project or library. It helps making the resulting package
# relocatable by avoiding hardcoded paths in the installed Config.cmake file.
#
# In a FooConfig.cmake file there may be code like this to make the
# install destinations know to the using project:
# set(FOO_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@" )
# set(FOO_DATA_DIR "@CMAKE_INSTALL_PREFIX@/@RELATIVE_DATA_INSTALL_DIR@" )
# set(FOO_ICONS_DIR "@CMAKE_INSTALL_PREFIX@/share/icons" )
# ...logic to determine installedPrefix from the own location...
# set(FOO_CONFIG_DIR "${installedPrefix}/@CONFIG_INSTALL_DIR@" )
# All 4 options shown above are not sufficient, since the first 3 hardcode
# the absolute directory locations, and the 4th case works only if the logic
# to determine the installedPrefix is correct, and if CONFIG_INSTALL_DIR contains
# a relative path, which in general cannot be guaranteed.
# This has the effect that the resulting FooConfig.cmake file would work poorly
# under Windows and OSX, where users are used to choose the install location
# of a binary package at install time, independent from how CMAKE_INSTALL_PREFIX
# was set at build/cmake time.
#
# Using CONFIGURE_PACKAGE_CONFIG_FILE() helps. If used correctly, it makes the
# resulting FooConfig.cmake file relocatable.
# Usage:
# 1. write a FooConfig.cmake.in file as you are used to
# 2. insert a line containing only the string "@PACKAGE_INIT@"
# 3. instead of set(FOO_DIR "@SOME_INSTALL_DIR@"), use set(FOO_DIR "@PACKAGE_SOME_INSTALL_DIR@")
# (this must be after the @PACKAGE_INIT@ line)
# 4. instead of using the normal configure_file(), use CONFIGURE_PACKAGE_CONFIG_FILE()
#
# The <input> and <output> arguments are the input and output file, the same way
# as in configure_file().
#
# The <path> given to INSTALL_DESTINATION must be the destination where the FooConfig.cmake
# file will be installed to. This can either be a relative or absolute path, both work.
#
# The variables <var1> to <varN> given as PATH_VARS are the variables which contain
# install destinations. For each of them the macro will create a helper variable
# PACKAGE_<var...>. These helper variables must be used
# in the FooConfig.cmake.in file for setting the installed location. They are calculated
# by CONFIGURE_PACKAGE_CONFIG_FILE() so that they are always relative to the
# installed location of the package. This works both for relative and also for absolute locations.
# For absolute locations it works only if the absolute location is a subdirectory
# of CMAKE_INSTALL_PREFIX.
#
# By default configure_package_config_file() also generates two helper macros,
# set_and_check() and check_required_components() into the FooConfig.cmake file.
#
# set_and_check() should be used instead of the normal set()
# command for setting directories and file locations. Additionally to setting the
# variable it also checks that the referenced file or directory actually exists
# and fails with a FATAL_ERROR otherwise. This makes sure that the created
# FooConfig.cmake file does not contain wrong references.
# When using the NO_SET_AND_CHECK_MACRO, this macro is not generated into the
# FooConfig.cmake file.
#
# check_required_components(<package_name>) should be called at the end of the
# FooConfig.cmake file if the package supports components.
# This macro checks whether all requested, non-optional components have been found,
# and if this is not the case, sets the Foo_FOUND variable to FALSE, so that the package
# is considered to be not found.
# It does that by testing the Foo_<Component>_FOUND variables for all requested
# required components.
# When using the NO_CHECK_REQUIRED_COMPONENTS option, this macro is not generated
# into the FooConfig.cmake file.
#
# For an example see below the documentation for WRITE_BASIC_PACKAGE_VERSION_FILE().
#
#
# WRITE_BASIC_PACKAGE_VERSION_FILE( filename VERSION major.minor.patch COMPATIBILITY (AnyNewerVersion|SameMajorVersion|ExactVersion) )
#
# Writes a file for use as <package>ConfigVersion.cmake file to <filename>.
# See the documentation of find_package() for details on this.
# filename is the output filename, it should be in the build tree.
# major.minor.patch is the version number of the project to be installed
# The COMPATIBILITY mode AnyNewerVersion means that the installed package version
# will be considered compatible if it is newer or exactly the same as the requested version.
# This mode should be used for packages which are fully backward compatible,
# also across major versions.
# If SameMajorVersion is used instead, then the behaviour differs from AnyNewerVersion
# in that the major version number must be the same as requested, e.g. version 2.0 will
# not be considered compatible if 1.0 is requested.
# This mode should be used for packages which guarantee backward compatibility within the
# same major version.
# If ExactVersion is used, then the package is only considered compatible if the requested
# version matches exactly its own version number (not considering the tweak version).
# For example, version 1.2.3 of a package is only considered compatible to requested version 1.2.3.
# This mode is for packages without compatibility guarantees.
# If your project has more elaborated version matching rules, you will need to write your
# own custom ConfigVersion.cmake file instead of using this macro.
#
# Internally, this macro executes configure_file() to create the resulting
# version file. Depending on the COMPATIBLITY, either the file
# BasicConfigVersion-SameMajorVersion.cmake.in or BasicConfigVersion-AnyNewerVersion.cmake.in
# is used. Please note that these two files are internal to CMake and you should
# not call configure_file() on them yourself, but they can be used as starting
-# point to create more sophisticted custom ConfigVersion.cmake files.
+# point to create more sophisticated custom ConfigVersion.cmake files.
#
#
# Example using both configure_package_config_file() and write_basic_package_version_file():
# CMakeLists.txt:
# set(INCLUDE_INSTALL_DIR include/ ... CACHE )
# set(LIB_INSTALL_DIR lib/ ... CACHE )
# set(SYSCONFIG_INSTALL_DIR etc/foo/ ... CACHE )
# ...
# include(CMakePackageConfigHelpers)
# configure_package_config_file(FooConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
# INSTALL_DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake
# PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR)
# write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
# VERSION 1.2.3
# COMPATIBILITY SameMajorVersion )
# install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
# DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake )
#
# With a FooConfig.cmake.in:
# set(FOO_VERSION x.y.z)
# ...
# @PACKAGE_INIT@
# ...
# set_and_check(FOO_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
# set_and_check(FOO_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
#
# check_required_components(Foo)
#=============================================================================
# Copyright 2012 Alexander Neundorf <neundorf@kde.org>
#
# CMake - Cross Platform Makefile Generator
# Copyright 2000-2011 Kitware, Inc., Insight Software Consortium
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ------------------------------------------------------------------------------
#
# The above copyright and license notice applies to distributions of
# CMake in source and binary form. Some source files contain additional
# notices of original copyright by their contributors; see each source
# for details. Third-party software packages supplied with CMake under
# compatible licenses provide their own copyright notices documented in
# corresponding subdirectories.
#
#------------------------------------------------------------------------------
#
# CMake was initially developed by Kitware with the following sponsorship:
#
# * National Library of Medicine at the National Institutes of Health
# as part of the Insight Segmentation and Registration Toolkit (ITK).
#
# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
# Visualization Initiative.
#
# * National Alliance for Medical Image Computing (NAMIC) is funded by the
# National Institutes of Health through the NIH Roadmap for Medical Research,
# Grant U54 EB005149.
#
# * Kitware, Inc.
#=============================================================================
include(CMakeParseArguments)
function(CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
set(oneValueArgs INSTALL_DESTINATION )
set(multiValueArgs PATH_VARS )
cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(CCF_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
endif()
if(NOT CCF_INSTALL_DESTINATION)
message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
endif()
if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
set(absInstallDir "${CCF_INSTALL_DESTINATION}")
else()
set(absInstallDir "${CMAKE_INSTALL_PREFIX}/${CCF_INSTALL_DESTINATION}")
endif()
file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${CMAKE_INSTALL_PREFIX}" )
foreach(var ${CCF_PATH_VARS})
if(NOT DEFINED ${var})
message(FATAL_ERROR "Variable ${var} does not exist")
else()
if(IS_ABSOLUTE "${${var}}")
string(REPLACE "${CMAKE_INSTALL_PREFIX}" "\${PACKAGE_PREFIX_DIR}"
PACKAGE_${var} "${${var}}")
else()
set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
endif()
endif()
endforeach()
get_filename_component(inputFileName "${_inputFile}" NAME)
set(PACKAGE_INIT "
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was ${inputFileName} ########
get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
")
if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
# Handle "/usr move" symlinks created by some Linux distros.
set(PACKAGE_INIT "${PACKAGE_INIT}
# Use original install prefix when loaded through a \"/usr move\"
# cross-prefix symbolic link such as /lib -> /usr/lib.
get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
if(_realCurr STREQUAL _realOrig)
set(PACKAGE_PREFIX_DIR \"${CMAKE_INSTALL_PREFIX}\")
endif()
unset(_realOrig)
unset(_realCurr)
")
endif()
if(NOT CCF_NO_SET_AND_CHECK_MACRO)
set(PACKAGE_INIT "${PACKAGE_INIT}
macro(set_and_check _var _file)
set(\${_var} \"\${_file}\")
if(NOT EXISTS \"\${_file}\")
message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
endif()
endmacro()
")
endif()
if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
set(PACKAGE_INIT "${PACKAGE_INIT}
macro(check_required_components _NAME)
foreach(comp \${\${_NAME}_FIND_COMPONENTS})
if(NOT \${_NAME}_\${comp}_FOUND)
if(\${_NAME}_FIND_REQUIRED_\${comp})
set(\${_NAME}_FOUND FALSE)
endif()
endif()
endforeach()
endmacro()
")
endif()
set(PACKAGE_INIT "${PACKAGE_INIT}
####################################################################################")
configure_file("${_inputFile}" "${_outputFile}" @ONLY)
endfunction()
diff --git a/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake b/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake
index 0ec97da19f..7f6e649d98 100644
--- a/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake
+++ b/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake
@@ -1,68 +1,68 @@
#
# Helper macro allowing to check if the given flags are supported
# by the underlying build tool
#
# If the flag(s) is/are supported, they will be appended to the string identified by RESULT_VAR
#
# Usage:
# usFunctionCheckCompilerFlags(FLAGS_TO_CHECK VALID_FLAGS_VAR)
#
# Example:
#
# set(myflags)
# usFunctionCheckCompilerFlags("-fprofile-arcs" myflags)
# message(1-myflags:${myflags})
# usFunctionCheckCompilerFlags("-fauto-bugfix" myflags)
# message(2-myflags:${myflags})
# usFunctionCheckCompilerFlags("-Wall" myflags)
# message(1-myflags:${myflags})
#
# The output will be:
# 1-myflags: -fprofile-arcs
# 2-myflags: -fprofile-arcs
# 3-myflags: -fprofile-arcs -Wall
include(CheckCXXCompilerFlag)
function(usFunctionCheckCompilerFlags FLAG_TO_TEST RESULT_VAR)
if(FLAG_TO_TEST STREQUAL "")
message(FATAL_ERROR "FLAG_TO_TEST shouldn't be empty")
endif()
# Save the contents of RESULT_VAR temporarily.
# This is needed in case ${RESULT_VAR} is one of the CMAKE_<LANG>_FLAGS_* variables.
set(_saved_result_var ${${RESULT_VAR}})
# Clear all flags. If not, existing flags triggering warnings might lead to
# false-negatives when checking for certain compiler flags.
set(CMAKE_C_FLAGS )
set(CMAKE_C_FLAGS_DEBUG )
set(CMAKE_C_FLAGS_MINSIZEREL )
set(CMAKE_C_FLAGS_RELEASE )
set(CMAKE_C_FLAGS_RELWITHDEBINFO )
set(CMAKE_CXX_FLAGS )
set(CMAKE_CXX_FLAGS_DEBUG )
set(CMAKE_CXX_FLAGS_MINSIZEREL )
set(CMAKE_CXX_FLAGS_RELEASE )
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO )
# Internally, the macro CMAKE_CXX_COMPILER_FLAG calls TRY_COMPILE. To avoid
# the cost of compiling the test each time the project is configured, the variable set by
# the macro is added to the cache so that following invocation of the macro with
# the same variable name skip the compilation step.
# For that same reason, the usFunctionCheckCompilerFlags function appends a unique suffix to
# the HAS_CXX_FLAG variable. This suffix is created using a 'clean version' of the
- # flag to test. The value of HAS_CXX_FLAG_${suffix} additonally needs to be a valid
+ # flag to test. The value of HAS_CXX_FLAG_${suffix} additionally needs to be a valid
# pre-processor token because CHECK_CXX_COMPILER_FLAG adds it as a definition to the compiler
# arguments. An invalid token triggers compiler warnings, which in case of the "-Werror" flag
# leads to false-negative checks.
string(REGEX REPLACE "[/-]" "_" suffix ${FLAG_TO_TEST})
string(REGEX REPLACE "[, \\$\\+\\*\\{\\}\\(\\)\\#]" "" suffix ${suffix})
CHECK_CXX_COMPILER_FLAG(${FLAG_TO_TEST} HAS_CXX_FLAG_${suffix})
if(HAS_CXX_FLAG_${suffix})
set(${RESULT_VAR} "${_saved_result_var} ${FLAG_TO_TEST}" PARENT_SCOPE)
endif()
endfunction()
diff --git a/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake b/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake
index cf23534b03..fd8fc75974 100644
--- a/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake
+++ b/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake
@@ -1,194 +1,194 @@
#! \ingroup MicroServicesCMake
#! \brief Embed resources in a library or executable.
#!
#! This CMake function uses an external command line program to generate a ZIP archive
#! containing data from external resources such as text files or images or other ZIP
#! archives. The created archive file is appended or embedded as a binary blob to the target file.
#!
#! \note To set-up correct file dependencies from your module target to your resource
#! files, you have to add a special source file to the source list of the target.
#! The source file name can be retrieved by using usFunctionGetResourceSoruce.
#! This ensures that changed resource files will automatically be re-added to the
#! module.
#!
-#! There are two differend modes for including resources: APPEND and LINK. In APPEND mode,
+#! There are two different modes for including resources: APPEND and LINK. In APPEND mode,
#! the generated zip file is appended at the end of the target file. In LINK mode, the
#! zip file is compiled / linked into the target using platform specific techniques. LINK
#! mode is necessary if certain tools make additional assumptions about the object layout
#! of the target file (e.g. codesign on MacOS). LINK mode may result in slower module
#! initialization and bigger object files. The default mode is LINK mode on MacOS and
#! APPEND mode on all other platforms.
#!
#! Example usage:
#! \code{.cmake}
#! set(module_srcs )
#! usFunctionEmbedResources(TARGET mylib
#! MODULE_NAME org_me_mylib
#! FILES config.properties logo.png
#! )
#! \endcode
#!
#! \param TARGET (required) The target to which the resource files are added.
#! \param MODULE_NAME (required/optional) The module name of the target, as specified in
#! the \c US_MODULE_NAME pre-processor definition of that target. This parameter
#! is optional if a target property with the name US_MODULE_NAME exists, containing
#! the required module name.
#! \param APPEND Append the resources zip file to the target file.
#! \param LINK Link (embed) the resources zip file if possible.
#!
#! For the WORKING_DIRECTORY, COMPRESSION_LEVEL, FILES, ZIP_ARCHIVES parameters see the
#! documentation of the usFunctionAddResources macro which is called with these parameters if set.
#!
#! \sa usFunctionAddResources
#! \sa usFunctionGetResourceSource
#! \sa \ref MicroServices_Resources
#!
function(usFunctionEmbedResources)
cmake_parse_arguments(US_RESOURCE "APPEND;LINK" "TARGET;MODULE_NAME;WORKING_DIRECTORY;COMPRESSION_LEVEL" "FILES;ZIP_ARCHIVES" ${ARGN})
if(NOT US_RESOURCE_TARGET)
message(SEND_ERROR "TARGET argument not specified.")
endif()
if(US_RESOURCE_FILES OR US_RESOURCE_ZIP_ARCHIVES)
usFunctionAddResources(TARGET ${US_RESOURCE_TARGET}
MODULE_NAME ${US_RESOURCE_MODULE_NAME}
WORKING_DIRECTORY ${US_RESOURCE_WORKING_DIRECTORY}
COMPRESSION_LEVEL ${US_RESOURCE_COMPRESSION_LEVEL}
FILES ${US_RESOURCE_FILES}
ZIP_ARCHIVES ${US_RESOURCE_ZIP_ARCHIVES}
)
endif()
get_target_property(_res_zips ${US_RESOURCE_TARGET} _us_resource_zips)
if(NOT _res_zips)
return()
endif()
if(US_RESOURCE_APPEND AND US_RESOURCE_LINK)
message(WARNING "Both APPEND and LINK options specified. Falling back to default behaviour.")
set(US_RESOURCE_APPEND 0)
set(US_RESOURCE_LINK 0)
endif()
if(US_RESOURCE_LINK AND NOT US_RESOURCE_LINKING_AVAILABLE)
message(WARNING "Resource linking not available. Falling back to APPEND mode.")
set(US_RESOURCE_LINK 0)
set(US_RESOURCE_APPEND 1)
endif()
# Set default resource mode
if(NOT US_RESOURCE_APPEND AND NOT US_RESOURCE_LINK)
if(US_DEFAULT_RESOURCE_MODE STREQUAL "LINK")
set(US_RESOURCE_LINK 1)
else()
set(US_RESOURCE_APPEND 1)
endif()
endif()
set(_mode )
if(US_RESOURCE_LINK)
set(_mode LINK)
elseif(US_RESOURCE_APPEND)
set(_mode APPEND)
endif()
usFunctionGetResourceSource(TARGET ${US_RESOURCE_TARGET} OUT _source_output ${_mode})
if(NOT US_RESOURCE_WORKING_DIRECTORY)
set(US_RESOURCE_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(NOT IS_ABSOLUTE ${US_RESOURCE_WORKING_DIRECTORY})
set(US_RESOURCE_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${US_RESOURCE_WORKING_DIRECTORY}")
endif()
set(resource_compiler ${US_RCC_EXECUTABLE})
if(TARGET ${US_RCC_EXECUTABLE_NAME})
set(resource_compiler ${US_RCC_EXECUTABLE_NAME})
elseif(NOT resource_compiler)
message(FATAL_ERROR "The CppMicroServices resource compiler was not found. Check the US_RCC_EXECUTABLE CMake variable.")
endif()
set(_zip_archive )
get_target_property(_counter ${US_RESOURCE_TARGET} _us_resource_counter)
if(_counter EQUAL 0)
set(_zip_archive ${_res_zips})
else()
set(_zip_archive ${CMAKE_CURRENT_BINARY_DIR}/us_${US_RESOURCE_TARGET}/res.zip)
add_custom_command(
OUTPUT ${_zip_archive}
COMMAND ${resource_compiler} ${_zip_archive} dummy -m ${_res_zips}
DEPENDS ${_res_zips} ${resource_compiler}
COMMENT "Creating resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
endif()
get_filename_component(_zip_archive_name ${_zip_archive} NAME)
get_filename_component(_zip_archive_path ${_zip_archive} PATH)
if(US_RESOURCE_LINK)
if(APPLE)
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_CXX_COMPILER} -c ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} -o stub.o
COMMAND ${CMAKE_LINKER} -r -sectcreate __TEXT us_resources ${_zip_archive_name} stub.o -o ${_source_output}
DEPENDS ${_zip_archive}
WORKING_DIRECTORY ${_zip_archive_path}
COMMENT "Linking resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
set_source_files_properties(${_source_output} PROPERTIES EXTERNAL_OBJECT 1 GENERATED 1)
elseif(WIN32 AND CMAKE_RC_COMPILER)
set(US_RESOURCE_ARCHIVE ${_zip_archive})
configure_file(${US_RESOURCE_RC_TEMPLATE} ${_source_output})
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_COMMAND} -E touch ${_source_output}
DEPENDS ${_zip_archive}
WORKING_DIRECTORY ${_zip_archive_path}
COMMENT "Linking resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
elseif(UNIX)
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_LINKER} -r -b binary -o ${_source_output} ${_zip_archive_name}
COMMAND objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents ${_source_output} ${_source_output}
DEPENDS ${_zip_archive}
WORKING_DIRECTORY ${_zip_archive_path}
COMMENT "Linking resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
set_source_files_properties(${_source_output} PROPERTIES EXTERNAL_OBJECT 1 GENERATED 1)
else()
message(WARNING "Internal error: Resource linking not available. Falling back to APPEND mode.")
set(US_RESOURCE_LINK 0)
set(US_RESOURCE_APPEND 1)
endif()
endif()
if(US_RESOURCE_APPEND)
# This command depends on the given resource files and creates a source
# file which must be added to the source list of the related target.
# This way, the following command is executed if the resources change
# and it just touches the created source file to force a (actually unnecessary)
# re-linking and hence the execution of POST_BUILD commands.
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_COMMAND} -E copy ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} ${_source_output}
DEPENDS ${_zip_archive}
COMMENT "Checking resource dependencies for ${US_RESOURCE_TARGET}"
VERBATIM
)
add_custom_command(
TARGET ${US_RESOURCE_TARGET}
POST_BUILD
COMMAND ${resource_compiler} --append $<TARGET_FILE:${US_RESOURCE_TARGET}> ${_zip_archive}
WORKING_DIRECTORY ${US_RESOURCE_WORKING_DIRECTORY}
COMMENT "Appending zipped resources to ${US_RESOURCE_TARGET}"
VERBATIM
)
endif()
endfunction()
diff --git a/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md b/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md
index 3c29df35a0..f056bd3e4e 100644
--- a/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md
+++ b/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md
@@ -1,92 +1,92 @@
Service Hooks {#MicroServices_ServiceHooks}
=============
The CppMicroServices library implements the Service Hook Service Specification Version 1.1 from
OSGi Core Release 5 for C++. Below is a summary of the concept - consult the OSGi specifications
for more details.
Service hooks provide mechanisms for module writers to closely interact with the CppMicroServices
service registry. These mechanisms are not intended for use by application modules but rather
by modules in need of *hooking* into the service registry and modifying the behaviour of
application modules.
Specific use case for service hooks include proxying of existing services by hiding the original
service and registering a *proxy service* with the same properties or providing services
*on demand* based on registered service listeners from external modules.
## Event Listener Hook
A module can intercept events being delivered to other modules by registering a ServiceEventListenerHook
object as a service. The CppMicroServices library will send all service events to all the registered
hooks using the reversed ordering of their ServiceReference objects. Note that event listener hooks
are called *after* the event was created but *before* it is filtered by the optional filter expression
of the service listeners. Hence an event listener hook receives all \link us::ServiceEvent::REGISTERED
REGISTERED \endlink, \link us::ServiceEvent::MODIFIED MODIFIED \endlink, \link us::ServiceEvent::UNREGISTERING
UNREGISTERING \endlink, and \link us::ServiceEvent::MODIFIED_ENDMATCH MODIFIED_ENDMATCH \endlink events
regardelss of the presence of a service listener filter. It may then remove modules or specific
service listeners from the ServiceEventListenerHook::ShrinkableMapType object passed to the
ServiceEventListenerHook::Event method to hide
service events.
Implementers of the Event Listener Hook must ensure that modules continue to see a consistent set of
service events.
## Find Hook
Find Hook objects registered using the ServiceFindHook interface will be called when modules look up
service references via the ModuleContext::GetServiceReference or ModuleContext::GetServiceReferences
methods. The order in which the CppMicroServices library calls the find hooks is the reverse `operator<`
ordering of their ServiceReference objects. The hooks may remove service references from the
ShrinkableVector object passed to the ServiceFindHook::Find method to hide services from specific modules.
## Listener Hook
The CppMicroServices API provides information about the registration, unregistration, and modification
of services. However, it does not directly allow the introspection of modules to get information about
what services a module is waiting for. Waiting for a service to arrive (via a registered service listener)
before performing its function is a common pattern for modules. Listener Hooks provide a mechanism to
-get informed about all existing, newly registerd, and removed service listeners.
+get informed about all existing, newly registered, and removed service listeners.
A Listener Hook object registered using the ServiceListenerHook interface will be notified about service
listeners by being passed ServiceListenerHook::ListenerInfo objects. Each ListenerInfo object is related to
the registration / unregistration cycle of a specific service listener. That is, registering the same service
-listener again, even with a different filter, will automatically unregister the previouse registration and
+listener again, even with a different filter, will automatically unregister the previous registration and
the newly registered service listener is related to a different ListenerInfo object. ListenerInfo objects
can be stored in unordered containers and compared with each other, e.g. to match ServiceListenerHook::Added
and ServiceListenerHook::Removed calls.
The Listener Hooks are called synchronously in the same order of their registration. However, in rare cases
the removal of a service listener may be reported before its corresponding addition. To handle this case,
the ListenerInfo::IsRemoved() method is provided which can be used in the ServiceListenerHook::Added
method to detect the out of order delivery. A simple strategy is to ignore removed events without
corresponding added events and ignore added events where the ListenerInfo object is already removed:
\snippet uServices-servicelistenerhook/main.cpp 1
## Architectural Notes
### Ordinary Services
All service hooks are treated as ordinary services. If the CppMicroServices library uses them, their
Service References will show that the CppMicroServices modules is using them, and if a hook is a
Service Factory, then the actual instance will be properly created.
The only speciality of the service hooks is that the CppMicroServices library does not use them for
the hooks themselves. That is, the Service Event and Service Find Hooks can not be used to hide the
services from the CppMicroServices library.
### Ordering
The hooks are very sensitive to ordering because they interact directly with the service registry.
In general, implementers of the hooks must be aware that other modules can be loaded before or after
the module which provides the hooks. To ensure early registration of the hooks, they should be registered
within the ModuleActivator::Load method of the program executable or a module being auto-loaded with
the executable.
### Multi Threading
All hooks must be thread-safe because the hooks can be called at any time. All hook methods must be
re-entrant, they can be entered at any time and in rare cases in the wrong order. The CppMicroServices
library calls all hook methods synchronously but the calls might be triggered from any user thread
interacting with the CppMicroServices API. The CppMicroServices API can be called from any of the
hook methods but implementers must be careful to not hold any lock while calling CppMicroServices methods.
diff --git a/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md b/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md
index c5150a55bc..0424d7c0fa 100644
--- a/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md
+++ b/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md
@@ -1,123 +1,123 @@
Example 6 - Spell Checker Service Module {#MicroServices_Example6}
========================================
In this example, we complicate things further by defining a new service that
uses an arbitrary number of dictionary services to perform its function. More
precisely, we define a spell checker service which will aggregate all dictionary
services and provide another service that allows us to spell check passages
using our underlying dictionary services to verify the spelling of words. Our
module will only provide the spell checker service if there are at least two
dictionary services available. First, we will start by defining the spell checker
service interface in a file called `spellcheckservice/ISpellCheckService.h`:
\snippet spellcheckservice/ISpellCheckService.h service
The service interface is quite simple, with only one method that needs to be
implemented. Because we provide an empty out-of-line destructor (defined in the
file `ISpellCheckService.cpp`) we must export the service interface by using the
module specific `SPELLCHECKSERVICE_EXPORT` macro.
In the following source code, the module needs to create a complete list of all
dictionary services; this is somewhat tricky and must be done carefully if done
-manually via service event listners. Our module makes use of the `ServiceTracker`
+manually via service event listeners. Our module makes use of the `ServiceTracker`
and `ServiceTrackerCustomizer` classes to robustly react to service events related
to dictionary services. The module activator of our module now additionally implements
the `ServiceTrackerCustomizer` class to be automatically notified of arriving, departing,
or modified dictionary services. In case of a newly added dictionary service, our
`ServiceTrackerCustomizer::AddingService()` implementation checks if a spell checker
service was already registered and if not registers a new `ISpellCheckService` instance
if at lead two dictionary services are available.
If the number of dictionary services drops below two, our `ServiceTrackerCustomizer`
implementation un-registers the previously registered spell checker service instance.
These actions must be performed in a synchronized manner to avoid interference from
service events originating from different threads. The implementation of our module
activator is done in a file called `spellcheckservice/Activator.cpp`:
\snippet spellcheckservice/Activator.cpp Activator
Note that we do not need to unregister the service in stop() method, because the
C++ Micro Services library will automatically do so for us. The spell checker service
that we have implemented is very simple; it simply parses a given passage into words
and then loops through all available dictionary services for each word until it
determines that the word is correct. Any incorrect words are added to an error list
that will be returned to the caller. This solution is not optimal and is only intended
for educational purposes.
\note In this example, the service interface and implementation are both
contained in one module which exports the interface class. However, service
implementations almost never need to be exported and in many use cases
it is beneficial to provide the service interface and its implementation(s)
in separate modules. In such a scenario, clients of a service will only
have a link-time dependency on the shared library providing the service interface
(because of the out-of-line destructor) but not on any modules containing
service implementations. This often leads to modules which do not export
any symbols at all and hence need to be loaded into the running process
manually or by using the \ref MicroServices_AutoLoading "auto-loading mechanism".
\note Due to the link dependency of our module to the module containing the
dictionary service interface as well as a default implementation for it, there
might be at least one dictionary service registered when our module is
loaded, depending on your linker settings (e.g. on Windows, the linker usually
by default optimizes the link dependency away since our module does not actually
use any symbols from the dictionaryservice module. On Linux however, the link
dependency is kept by default.) To observe the dynamic registration and
un-registration of our spell checker service, we require the availability of
at least two dictionary services.
For an introduction how to compile our source code, see \ref MicroServices_Example1.
After running the `usCoreExamplesDriver` program we should make sure that the
module from Example 1 is active. We can use the `s` shell command to get
a list of all modules, their state, and their module identifier number.
If the Example 1 module is not active, we should load the module using the
load command and the module's identifier number or name that is displayed
by the `s` command. Now we can load the spell checker service module by
entering the `l spellcheckservice` command which will also trigger the loading
of the dictionaryservice module containing the english dictionary:
\verbatim
CppMicroServices-build> bin/usCoreExamplesDriver
> l eventlistener
Starting to listen for service events.
> l spellcheckservice
Ex1: Service of type IDictionaryService/1.0 registered.
> s
Id | Name | Status
-----------------------------------
- | dictionaryclient | -
- | dictionaryclient2 | -
- | dictionaryclient3 | -
- | frenchdictionary | -
- | spellcheckclient | -
1 | CppMicroServices | LOADED
2 | Event Listener | LOADED
3 | Dictionary Service | LOADED
4 | Spell Check Service | LOADED
>
\endverbatim
To trigger the registration of the spell checker service from our module, we
load the frenchdictionary using the `l frenchdictionary` command. If the module from
\ref MicroServices_Example1 "Example 1" is still active,
then we should see it print out the details of the service event it receives
when our new module registers its spell checker service:
\verbatim
CppMicroServices-build> bin/usCoreExamplesDriver
> l frenchdictionary
Ex1: Service of type IDictionaryService/1.0 registered.
Ex1: Service of type ISpellCheckService/1.0 registered.
>
\endverbatim
We can experiment with our spell checker service's dynamic availability by stopping
the french dictionary service; when the service is stopped,
the eventlistener module will print that our module is no longer offering its
spell checker service. Likewise, when the french dictionary service comes back, so will
our spell checker service. We create a client for our spell checker service in
\ref MicroServices_Example7 "Example 7". To exit the `usCoreExamplesDriver` program, we
use the `q` command.
Next: \ref MicroServices_Example7
Previous: \ref MicroServices_Example5
diff --git a/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h b/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h
index f80aae1cbc..aa49a50788 100644
--- a/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h
+++ b/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h
@@ -1,60 +1,60 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef IDICTIONARYSERVICE_H
#define IDICTIONARYSERVICE_H
//! [service]
#include <usServiceInterface.h>
#include <string>
#ifdef US_BUILD_SHARED_LIBS
#ifdef Example_dictionaryservice_EXPORTS
#define DICTIONARYSERVICE_EXPORT US_ABI_EXPORT
#else
#define DICTIONARYSERVICE_EXPORT US_ABI_IMPORT
#endif
#else
#define DICTIONARYSERVICE_EXPORT US_ABI_EXPORT
#endif
/**
* A simple service interface that defines a dictionary service.
* A dictionary service simply verifies the existence of a word.
**/
struct DICTIONARYSERVICE_EXPORT IDictionaryService
{
- // Out-of-line virtual desctructor for proper dynamic cast
+ // Out-of-line virtual destructor for proper dynamic cast
// support with older versions of gcc.
virtual ~IDictionaryService();
/**
* Check for the existence of a word.
* @param word the word to be checked.
* @return true if the word is in the dictionary,
* false otherwise.
**/
virtual bool CheckWord(const std::string& word) = 0;
};
//! [service]
#endif // DICTIONARYSERVICE_H
diff --git a/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h b/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h
index d72b59fe8e..d54acbaadd 100644
--- a/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h
+++ b/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h
@@ -1,66 +1,66 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef ISPELLCHECKSERVICE_H
#define ISPELLCHECKSERVICE_H
//! [service]
#include <usServiceInterface.h>
#include <string>
#include <vector>
#ifdef US_BUILD_SHARED_LIBS
#ifdef Example_spellcheckservice_EXPORTS
#define SPELLCHECKSERVICE_EXPORT US_ABI_EXPORT
#else
#define SPELLCHECKSERVICE_EXPORT US_ABI_IMPORT
#endif
#else
#define SPELLCHECKSERVICE_EXPORT US_ABI_EXPORT
#endif
/**
* A simple service interface that defines a spell check service. A spell check
* service checks the spelling of all words in a given passage. A passage is any
* number of words separated by a space character and the following punctuation
* marks: comma, period, exclamation mark, question mark, semi-colon, and colon.
*/
struct SPELLCHECKSERVICE_EXPORT ISpellCheckService
{
- // Out-of-line virtual desctructor for proper dynamic cast
+ // Out-of-line virtual destructor for proper dynamic cast
// support with older versions of gcc.
virtual ~ISpellCheckService();
/**
* Checks a given passage for spelling errors. A passage is any number of
* words separated by a space and any of the following punctuation marks:
* comma (,), period (.), exclamation mark (!), question mark (?),
* semi-colon (;), and colon(:).
*
* @param passage the passage to spell check.
* @return A list of misspelled words.
*/
virtual std::vector<std::string> Check(const std::string& passage) = 0;
};
//! [service]
//!
#endif // ISPELLCHECKSERVICE_H
diff --git a/Modules/CppMicroServices/core/include/usCoreConfig.h.in b/Modules/CppMicroServices/core/include/usCoreConfig.h.in
index fab29d715c..e3498eff6f 100644
--- a/Modules/CppMicroServices/core/include/usCoreConfig.h.in
+++ b/Modules/CppMicroServices/core/include/usCoreConfig.h.in
@@ -1,43 +1,43 @@
/*
usCoreConfig.h
This file is generated. Do not change!
*/
#ifndef USCORECONFIG_H
#define USCORECONFIG_H
#include <usCoreExport.h>
#cmakedefine US_ENABLE_AUTOLOADING_SUPPORT
///-------------------------------------------------------------------
// Version information
//-------------------------------------------------------------------
#define US_CORE_MAJOR_VERSION @Core_MAJOR_VERSION@
#define US_CORE_MINOR_VERSION @Core_MINOR_VERSION@
#define US_CORE_PATCH_VERSION @Core_PATCH_VERSION@
#define US_CORE_VERSION @Core_VERSION@
#define US_CORE_VERSION_STR "@Core_VERSION@"
//-------------------------------------------------------------------
-// Debuging & Logging
+// Debugging & Logging
//-------------------------------------------------------------------
#cmakedefine US_ENABLE_DEBUG_OUTPUT
US_BEGIN_NAMESPACE
enum MsgType { DebugMsg = 0, InfoMsg = 1, WarningMsg = 2, ErrorMsg = 3 };
typedef void (*MsgHandler)(MsgType, const char *);
US_Core_EXPORT MsgHandler installMsgHandler(MsgHandler);
US_END_NAMESPACE
///-------------------------------------------------------------------
// Macros used by the unit tests
//-------------------------------------------------------------------
#define US_CORE_SOURCE_DIR "@PROJECT_SOURCE_DIR@"
#endif // USCORECONFIG_H
diff --git a/Modules/CppMicroServices/core/include/usModule.h b/Modules/CppMicroServices/core/include/usModule.h
index 163f574ccc..be59522d55 100644
--- a/Modules/CppMicroServices/core/include/usModule.h
+++ b/Modules/CppMicroServices/core/include/usModule.h
@@ -1,367 +1,367 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USMODULE_H
#define USMODULE_H
#include "usModuleVersion.h"
#include <vector>
US_BEGIN_NAMESPACE
class Any;
class CoreModuleContext;
struct ModuleInfo;
class ModuleContext;
class ModuleResource;
class ModulePrivate;
template<class S>
class ServiceReference;
typedef ServiceReference<void> ServiceReferenceU;
/**
* \ingroup MicroServices
*
* Represents a CppMicroServices module.
*
* <p>
* A <code>%Module</code> object is the access point to a CppMicroServices module.
* Each CppMicroServices module has an associated <code>%Module</code> object.
*
* <p>
* A module has unique identity, a <code>long</code>, chosen by the
* framework. This identity does not change during the lifecycle of a module.
*
* <p>
* A module can be in one of two states:
* <ul>
* <li>LOADED
* <li>UNLOADED
* </ul>
* <p>
* You can determine the current state by using IsLoaded().
*
* <p>
* A module can only execute code when its state is <code>LOADED</code>.
* An <code>UNLOADED</code> module is a
* zombie and can only be reached because it was loaded before. However,
* unloaded modules can be loaded again.
*
* <p>
* The framework is the only entity that is allowed to create
* <code>%Module</code> objects.
*
* @remarks This class is thread safe.
*/
class US_Core_EXPORT Module
{
public:
/**
* Returns the property key for looking up this module's id.
* The property value is of type \c long.
*
* @return The id property key.
*/
static const std::string& PROP_ID();
/**
* Returns the property key for looking up this module's name.
* The property value is of type \c std::string.
*
* @return The name property key.
*/
static const std::string& PROP_NAME();
/**
* Returns the property key for looking up this module's
* location in the file system.
* The property value is of type \c std::string.
*
* @return The location property key.
*/
static const std::string& PROP_LOCATION();
/**
* Returns the property key with a value of \c module.version for looking
* up this module's version identifier.
* The property value is of type \c std::string.
*
* @return The version property key.
*/
static const std::string& PROP_VERSION();
/**
* Returns the property key with a value of \c module.vendor for looking
* up this module's vendor information.
* The property value is of type \c std::string.
*
* @return The vendor property key.
*/
static const std::string& PROP_VENDOR();
/**
* Returns the property key with a value of \c module.description for looking
* up this module's description.
* The property value is of type \c std::string.
*
* @return The description property key.
*/
static const std::string& PROP_DESCRIPTION();
/**
* Returns the property key with a value of \c module.autoload_dir for looking
* up this module's auto-load directory.
* The property value is of type \c std::string.
*
* @return The auto-load directory property key.
*/
static const std::string& PROP_AUTOLOAD_DIR();
/**
* Returns the property key with a value of \c module.autoloaded_modules for
* looking up this module's auto-load modules.
* The property value is of type \c std::vector<std::string> and contains
* the file system locations for the auto-loaded modules triggered by this
* module.
*
* @return The auto-loaded modules property key.
*/
static const std::string& PROP_AUTOLOADED_MODULES();
~Module();
/**
* Returns this module's current state.
*
* <p>
* A module can be in only one state at any time.
*
* @return <code>true</code> if the module is <code>LOADED</code>
* <code>false</code> if it is <code>UNLOADED</code>
*/
bool IsLoaded() const;
/**
* Returns this module's {@link ModuleContext}. The returned
* <code>ModuleContext</code> can be used by the caller to act on behalf
* of this module.
*
* <p>
* If this module is not in the <code>LOADED</code> state, then this
* module has no valid <code>ModuleContext</code>. This method will
* return <code>0</code> if this module has no valid
* <code>ModuleContext</code>.
*
* @return A <code>ModuleContext</code> for this module or
* <code>0</code> if this module has no valid
* <code>ModuleContext</code>.
*/
ModuleContext* GetModuleContext() const;
/**
* Returns this module's unique identifier. This module is assigned a unique
* identifier by the framework when it was loaded.
*
* <p>
* A module's unique identifier has the following attributes:
* <ul>
* <li>Is unique.
* <li>Is a <code>long</code>.
* <li>Its value is not reused for another module, even after a module is
* unloaded.
* <li>Does not change while a module remains loaded.
* <li>Does not change when a module is reloaded.
* </ul>
*
* <p>
* This method continues to return this module's unique identifier while
* this module is in the <code>UNLOADED</code> state.
*
* @return The unique identifier of this module.
*/
long GetModuleId() const;
/**
* Returns this module's location.
*
* <p>
* The location is the full path to the module's shared library.
* This method continues to return this module's location
* while this module is in the <code>UNLOADED</code> state.
*
* @return The string representation of this module's location.
*/
std::string GetLocation() const;
/**
* Returns the name of this module as specified by the
* US_CREATE_MODULE CMake macro. The module
* name together with a version must identify a unique module.
*
* <p>
* This method continues to return this module's name while
* this module is in the <code>UNLOADED</code> state.
*
* @return The name of this module.
*/
std::string GetName() const;
/**
* Returns the version of this module as specified by the
* US_INITIALIZE_MODULE CMake macro. If this module does not have a
* specified version then {@link ModuleVersion::EmptyVersion} is returned.
*
* <p>
* This method continues to return this module's version while
* this module is in the <code>UNLOADED</code> state.
*
* @return The version of this module.
*/
ModuleVersion GetVersion() const;
/**
* Returns the value of the specified property for this module. The
* method returns an empty Any if the property is not found.
*
* @param key The name of the requested property.
* @return The value of the requested property, or an empty string
* if the property is undefined.
*
* @sa GetPropertyKeys()
* @sa \ref MicroServices_ModuleProperties
*/
Any GetProperty(const std::string& key) const;
/**
* Returns a list of top-level property keys for this module.
*
* @return A list of available property keys.
*
* @sa \ref MicroServices_ModuleProperties
*/
std::vector<std::string> GetPropertyKeys() const;
/**
* Returns this module's ServiceReference list for all services it
* has registered or an empty list if this module has no registered
* services.
*
* The list is valid at the time of the call to this method, however,
* as the framework is a very dynamic environment, services can be
* modified or unregistered at anytime.
*
* @return A list of ServiceReference objects for services this
* module has registered.
*/
std::vector<ServiceReferenceU> GetRegisteredServices() const;
/**
* Returns this module's ServiceReference list for all services it is
* using or returns an empty list if this module is not using any
* services. A module is considered to be using a service if its use
* count for that service is greater than zero.
*
* The list is valid at the time of the call to this method, however,
* as the framework is a very dynamic environment, services can be
* modified or unregistered at anytime.
*
* @return A list of ServiceReference objects for all services this
* module is using.
*/
std::vector<ServiceReferenceU> GetServicesInUse() const;
/**
* Returns the resource at the specified \c path in this module.
* The specified \c path is always relative to the root of this module and may
* begin with '/'. A path value of "/" indicates the root of this module.
*
* @param path The path name of the resource.
* @return A ModuleResource object for the given \c path. If the \c path cannot
* be found in this module or the module's state is \c UNLOADED, an invalid
* ModuleResource object is returned.
*/
ModuleResource GetResource(const std::string& path) const;
/**
* Returns resources in this module.
*
* This method is intended to be used to obtain configuration, setup, localization
* and other information from this module.
*
* This method can either return only resources in the specified \c path or recurse
* into subdirectories returning resources in the directory tree beginning at the
* specified path.
*
* Examples:
* \snippet uServices-resources/main.cpp 0
*
* @param path The path name in which to look. The path is always relative to the root
* of this module and may begin with '/'. A path value of "/" indicates the root of this module.
* @param filePattern The resource name pattern for selecting entries in the specified path.
* The pattern is only matched against the last element of the resource path. Substring
- * matching is supported using the wildcard charachter ('*'). If \c filePattern is empty,
+ * matching is supported using the wildcard character ('*'). If \c filePattern is empty,
* this is equivalent to "*" and matches all resources.
* @param recurse If \c true, recurse into subdirectories. Otherwise only return resources
* from the specified path.
* @return A vector of ModuleResource objects for each matching entry.
*/
std::vector<ModuleResource> FindResources(const std::string& path, const std::string& filePattern, bool recurse) const;
private:
friend class CoreModuleActivator;
friend class ModuleRegistry;
friend class ServiceReferencePrivate;
ModulePrivate* d;
Module();
void Init(CoreModuleContext* coreCtx, ModuleInfo* info);
void Uninit();
void Start();
void Stop();
// purposely not implemented
Module(const Module &);
Module& operator=(const Module&);
};
US_END_NAMESPACE
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(Module)& module);
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, US_PREPEND_NAMESPACE(Module) const * module);
#endif // USMODULE_H
diff --git a/Modules/CppMicroServices/core/include/usModuleContext.h b/Modules/CppMicroServices/core/include/usModuleContext.h
index bfee00510d..63a2455273 100644
--- a/Modules/CppMicroServices/core/include/usModuleContext.h
+++ b/Modules/CppMicroServices/core/include/usModuleContext.h
@@ -1,851 +1,851 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USMODULECONTEXT_H_
#define USMODULECONTEXT_H_
// TODO: Replace includes with forward directives!
#include "usListenerFunctors_p.h"
#include "usServiceInterface.h"
#include "usServiceEvent.h"
#include "usServiceRegistration.h"
#include "usServiceException.h"
#include "usModuleEvent.h"
US_BEGIN_NAMESPACE
typedef US_SERVICE_LISTENER_FUNCTOR ServiceListener;
typedef US_MODULE_LISTENER_FUNCTOR ModuleListener;
class ModuleContextPrivate;
class ServiceFactory;
template<class S> class ServiceObjects;
/**
* \ingroup MicroServices
*
* A module's execution context within the framework. The context is used to
* grant access to other methods so that this module can interact with the
* Micro Services Framework.
*
* <p>
* <code>ModuleContext</code> methods allow a module to:
* <ul>
* <li>Subscribe to events published by the framework.
* <li>Register service objects with the framework service registry.
* <li>Retrieve <code>ServiceReference</code>s from the framework service
* registry.
* <li>Get and release service objects for a referenced service.
* <li>Get the list of modules loaded in the framework.
* <li>Get the {@link Module} object for a module.
* </ul>
*
* <p>
* A <code>ModuleContext</code> object will be created and provided to the
* module associated with this context when it is loaded using the
* us::ModuleActivator::Load method. The same <code>ModuleContext</code>
* object will be passed to the module associated with this context when it is
* unloaded using the us::ModuleActivator::Unload method. A
* <code>ModuleContext</code> object is generally for the private use of its
* associated module and is not meant to be shared with other modules in the
* module environment.
*
* <p>
* The <code>Module</code> object associated with a <code>ModuleContext</code>
* object is called the <em>context module</em>.
*
* <p>
* The <code>ModuleContext</code> object is only valid during the execution of
* its context module; that is, during the period when the context module
* is loaded. If the <code>ModuleContext</code>
* object is used subsequently, a <code>std::logic_error</code> is
* thrown. The <code>ModuleContext</code> object is never reused after
* its context module is unloaded.
*
* <p>
* The framework is the only entity that can create <code>ModuleContext</code>
* objects.
*
* @remarks This class is thread safe.
*/
class US_Core_EXPORT ModuleContext
{
public:
~ModuleContext();
/**
* Returns the <code>Module</code> object associated with this
* <code>ModuleContext</code>. This module is called the context module.
*
* @return The <code>Module</code> object associated with this
* <code>ModuleContext</code>.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
*/
Module* GetModule() const;
/**
* Returns the module with the specified identifier.
*
* @param id The identifier of the module to retrieve.
* @return A <code>Module</code> object or <code>0</code> if the
* identifier does not match any previously loaded module.
*/
Module* GetModule(long id) const;
/**
* Get the module that with the specified module name.
*
* @param name The name of the module to get.
* @return The requested \c Module or \c nullptr.
*/
Module* GetModule(const std::string& name);
/**
* Returns a list of all known modules.
* <p>
* This method returns a list of all modules loaded in the module
* environment at the time of the call to this method. This list will
* also contain modules which might already have been unloaded.
*
* @return A std::vector of <code>Module</code> objects which
* will hold one object per known module.
*/
std::vector<Module*> GetModules() const;
/**
* Registers the specified service object with the specified properties
* under the specified class names into the framework. A
* <code>ServiceRegistration</code> object is returned. The
* <code>ServiceRegistration</code> object is for the private use of the
* module registering the service and should not be shared with other
* modules. The registering module is defined to be the context module.
* Other modules can locate the service by using either the
* GetServiceReferences() or GetServiceReference() method.
*
* <p>
* A module can register a service object that implements the
* ServiceFactory or PrototypeServiceFactory interface to have more
* flexibility in providing service objects to other modules.
*
* <p>
* The following steps are taken when registering a service:
* <ol>
* <li>The framework adds the following service properties to the service
* properties from the specified <code>ServiceProperties</code> (which may be
* omitted): <br/>
* A property named us::ServiceConstants::SERVICE_ID() identifying the
* registration number of the service <br/>
* A property named us::ServiceConstants::OBJECTCLASS() containing all the
* specified classes. <br/>
* A property named us::ServiceConstants::SERVICE_SCOPE() identifying the scope
* of the service. <br/>
* Properties with these names in the specified <code>ServiceProperties</code> will
* be ignored.
* <li>The service is added to the framework service registry and may now be
* used by other modules.
* <li>A service event of type ServiceEvent#REGISTERED is fired.
* <li>A <code>ServiceRegistration</code> object for this registration is
* returned.
* </ol>
*
* @note This is a low-level method and should normally not be used directly.
* Use one of the templated RegisterService methods instead.
*
* @param service The service object, which is a map of interface identifiers
* to raw service pointers.
* @param properties The properties for this service. The keys in the
* properties object must all be <code>std::string</code> objects. See
* us::ServiceConstants for a list of standard service property keys.
* Changes should not be made to this object after calling this
* method. To update the service's properties the
* us::ServiceRegistration::SetProperties method must be called.
* The set of properties may be omitted if the service has
* no properties.
* @return A <code>ServiceRegistration</code> object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
*
* @throws std::invalid_argument If one of the following is true:
* <ul>
* <li><code>service</code> is <code>0</code>.
* <li><code>properties</code> contains case variants of the same key name.
* </ul>
* @throws std::logic_error If this ModuleContext is no longer valid.
*
* @see ServiceRegistration
* @see ServiceFactory
* @see PrototypeServiceFactory
*/
ServiceRegistrationU RegisterService(const InterfaceMap& service,
const ServiceProperties& properties = ServiceProperties());
/**
* Registers the specified service object with the specified properties
* using the specified template argument with the framework.
*
* <p>
* This method is provided as a convenience when <code>service</code> will only be registered under
* a single class name whose type is available to the caller. It is otherwise identical to
* RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred
* since it avoids errors in the string literal identifying the class name or interface identifier.
*
* Example usage:
* \snippet uServices-registration/main.cpp 1-1
* \snippet uServices-registration/main.cpp 1-2
*
* @tparam S The type under which the service can be located.
* @param service The service object or a ServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class S>
ServiceRegistration<S> RegisterService(S* service, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<S>(service);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service object with the specified properties
* using the specified template argument with the framework.
*
* <p>
* This method is provided as a convenience when registering a service under
* two interface classes whose type is available to the caller. It is otherwise identical to
* RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred
* since it avoids errors in the string literal identifying the class name or interface identifier.
*
* Example usage:
* \snippet uServices-registration/main.cpp 2-1
* \snippet uServices-registration/main.cpp 2-2
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @param impl The service object or a ServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2, class Impl>
ServiceRegistration<I1,I2> RegisterService(Impl* impl, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1, I2>(impl);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service object with the specified properties
* using the specified template argument with the framework.
*
* <p>
* This method is identical to the RegisterService<I1,I2,Impl>(Impl*, const ServiceProperties&)
* method except that it supports three service interface types.
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @tparam I3 The third interface type under which the service can be located.
* @param impl The service object or a ServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2, class I3, class Impl>
ServiceRegistration<I1,I2,I3> RegisterService(Impl* impl, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1, I2, I3>(impl);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service factory as a service with the specified properties
* using the specified template argument as service interface type with the framework.
*
* <p>
* This method is provided as a convenience when <code>factory</code> will only be registered under
* a single class name whose type is available to the caller. It is otherwise identical to
* RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred
* since it avoids errors in the string literal identifying the class name or interface identifier.
*
* Example usage:
* \snippet uServices-registration/main.cpp 1-1
* \snippet uServices-registration/main.cpp f1
*
* @tparam S The type under which the service can be located.
* @param factory The ServiceFactory or PrototypeServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service factory object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class S>
ServiceRegistration<S> RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<S>(factory);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service factory as a service with the specified properties
* using the specified template argument as service interface type with the framework.
*
* <p>
* This method is identical to the RegisterService<S>(ServiceFactory*, const ServiceProperties&)
* method except that it supports two service interface types.
*
* Example usage:
* \snippet uServices-registration/main.cpp 2-1
* \snippet uServices-registration/main.cpp f2
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @param factory The ServiceFactory or PrototypeServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service factory object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2>
ServiceRegistration<I1,I2> RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1,I2>(factory);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service factory as a service with the specified properties
* using the specified template argument as service interface type with the framework.
*
* <p>
* This method is identical to the RegisterService<S>(ServiceFactory*, const ServiceProperties&)
* method except that it supports three service interface types.
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @tparam I3 The third interface type under which the service can be located.
* @param factory The ServiceFactory or PrototypeServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service factory object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2, class I3>
ServiceRegistration<I1,I2,I3> RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1,I2,I3>(factory);
return RegisterService(servicePointers, properties);
}
/**
* Returns a list of <code>ServiceReference</code> objects. The returned
* list contains services that
* were registered under the specified class and match the specified filter
* expression.
*
* <p>
* The list is valid at the time of the call to this method. However since
* the Micro Services framework is a very dynamic environment, services can be modified or
* unregistered at any time.
*
* <p>
* The specified <code>filter</code> expression is used to select the
* registered services whose service properties contain keys and values
* which satisfy the filter expression. See LDAPFilter for a description
* of the filter syntax. If the specified <code>filter</code> is
* empty, all registered services are considered to match the
* filter. If the specified <code>filter</code> expression cannot be parsed,
* an <code>std::invalid_argument</code> will be thrown with a human readable
* message where the filter became unparsable.
*
* <p>
* The result is a list of <code>ServiceReference</code> objects for all
* services that meet all of the following conditions:
* <ul>
* <li>If the specified class name, <code>clazz</code>, is not
* empty, the service must have been registered with the
* specified class name. The complete list of class names with which a
* service was registered is available from the service's
* us::ServiceConstants::OBJECTCLASS() property.
* <li>If the specified <code>filter</code> is not empty, the
* filter expression must match the service.
* </ul>
*
* @param clazz The class name with which the service was registered or
* an empty string for all services.
* @param filter The filter expression or empty for all
* services.
* @return A list of <code>ServiceReference</code> objects or
* an empty list if no services are registered which satisfy the
* search.
* @throws std::invalid_argument If the specified <code>filter</code>
* contains an invalid filter expression that cannot be parsed.
* @throws std::logic_error If this ModuleContext is no longer valid.
*/
std::vector<ServiceReferenceU> GetServiceReferences(const std::string& clazz, const std::string& filter = std::string());
/**
* Returns a list of <code>ServiceReference</code> objects. The returned
* list contains services that
* were registered under the interface id of the template argument <code>S</code>
* and match the specified filter expression.
*
* <p>
* This method is identical to GetServiceReferences(const std::string&, const std::string&) except that
* the class name for the service object is automatically deduced from the template argument.
*
* @tparam S The type under which the requested service objects must have been registered.
* @param filter The filter expression or empty for all
* services.
* @return A list of <code>ServiceReference</code> objects or
* an empty list if no services are registered which satisfy the
* search.
* @throws std::invalid_argument If the specified <code>filter</code>
* contains an invalid filter expression that cannot be parsed.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid.
*
* @see GetServiceReferences(const std::string&, const std::string&)
*/
template<class S>
std::vector<ServiceReference<S> > GetServiceReferences(const std::string& filter = std::string())
{
std::string clazz = us_service_interface_iid<S>();
if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro");
typedef std::vector<ServiceReferenceU> BaseVectorT;
BaseVectorT serviceRefs = GetServiceReferences(clazz, filter);
std::vector<ServiceReference<S> > result;
for(BaseVectorT::const_iterator i = serviceRefs.begin(); i != serviceRefs.end(); ++i)
{
result.push_back(ServiceReference<S>(*i));
}
return result;
}
/**
* Returns a <code>ServiceReference</code> object for a service that
* implements and was registered under the specified class.
*
* <p>
* The returned <code>ServiceReference</code> object is valid at the time of
* the call to this method. However as the Micro Services framework is a very dynamic
* environment, services can be modified or unregistered at any time.
*
* <p>
* This method is the same as calling
* {@link ModuleContext::GetServiceReferences(const std::string&, const std::string&)} with an
* empty filter expression. It is provided as a convenience for
* when the caller is interested in any service that implements the
* specified class.
* <p>
* If multiple such services exist, the service with the highest ranking (as
* specified in its us::ServiceConstants::SERVICE_RANKING() property) is returned.
* <p>
* If there is a tie in ranking, the service with the lowest service ID (as
* specified in its us::ServiceConstants::SERVICE_ID() property); that is, the
* service that was registered first is returned.
*
* @param clazz The class name with which the service was registered.
* @return A <code>ServiceReference</code> object, or an invalid <code>ServiceReference</code> if
* no services are registered which implement the named class.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If no service was registered under the given class name.
*
* @see #GetServiceReferences(const std::string&, const std::string&)
*/
ServiceReferenceU GetServiceReference(const std::string& clazz);
/**
* Returns a <code>ServiceReference</code> object for a service that
* implements and was registered under the specified template class argument.
*
* <p>
* This method is identical to GetServiceReference(const std::string&) except that
* the class name for the service object is automatically deduced from the template argument.
*
* @tparam S The type under which the requested service must have been registered.
* @return A <code>ServiceReference</code> object, or an invalid <code>ServiceReference</code> if
* no services are registered which implement the type <code>S</code>.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException It no service was registered under the given class name.
* @see #GetServiceReference(const std::string&)
* @see #GetServiceReferences(const std::string&)
*/
template<class S>
ServiceReference<S> GetServiceReference()
{
std::string clazz = us_service_interface_iid<S>();
if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro");
return ServiceReference<S>(GetServiceReference(clazz));
}
/**
* Returns the service object referenced by the specified
* <code>ServiceReferenceBase</code> object.
* <p>
* A module's use of a service is tracked by the module's use count of that
* service. Each time a service's service object is returned by
* {@link #GetService(const ServiceReference<S>&)} the context module's use count for
* that service is incremented by one. Each time the service is released by
* {@link #UngetService(const ServiceReferenceBase&)} the context module's use count
* for that service is decremented by one.
* <p>
* When a module's use count for a service drops to zero, the module should
* no longer use that service.
*
* <p>
* This method will always return <code>0</code> when the service
* associated with this <code>reference</code> has been unregistered.
*
* <p>
* The following steps are taken to get the service object:
* <ol>
* <li>If the service has been unregistered, <code>0</code> is returned.
* <li>The context module's use count for this service is incremented by
* one.
* <li>If the context module's use count for the service is currently one
* and the service was registered with an object implementing the
* <code>ServiceFactory</code> interface, the
* us::ServiceFactory::GetService() method is
* called to create a service object for the context module. This service
* object is cached by the framework. While the context module's use count
* for the service is greater than zero, subsequent calls to get the
* services's service object for the context module will return the cached
* service object. <br>
* If the <code>ServiceFactory</code> object throws an
* exception, <code>0</code> is returned and a warning is logged.
* <li>The service object for the service is returned.
* </ol>
*
* @param reference A reference to the service.
* @return A service object for the service associated with
* <code>reference</code> or <code>0</code> if the service is not
* registered or the <code>ServiceFactory</code> threw
* an exception.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @throws std::invalid_argument If the specified
* <code>ServiceReferenceBase</code> is invalid (default constructed).
* @see #UngetService(const ServiceReferenceBase&)
* @see ServiceFactory
*/
void* GetService(const ServiceReferenceBase& reference);
InterfaceMap GetService(const ServiceReferenceU& reference);
/**
* Returns the service object referenced by the specified
* <code>ServiceReference</code> object.
* <p>
* This is a convenience method which is identical to void* GetService(const ServiceReferenceBase&)
* except that it casts the service object to the supplied template argument type
*
* @tparam S The type the service object will be cast to.
* @return A service object for the service associated with
* <code>reference</code> or <code>0</code> if the service is not
* registered, the <code>ServiceFactory</code> threw
* an exception or the service could not be casted to the desired type.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @throws std::invalid_argument If the specified
* <code>ServiceReference</code> is invalid (default constructed).
* @see #GetService(const ServiceReferenceBase&)
* @see #UngetService(const ServiceReferenceBase&)
* @see ServiceFactory
*/
template<class S>
S* GetService(const ServiceReference<S>& reference)
{
const ServiceReferenceBase& baseRef = reference;
return reinterpret_cast<S*>(GetService(baseRef));
}
/**
* Returns the ServiceObjects object for the service referenced by the specified
* ServiceReference object. The ServiceObjects object can be used to obtain
* multiple service objects for services with prototype scope. For services with
* singleton or module scope, the ServiceObjects::GetService() method behaves
* the same as the GetService(const ServiceReference<S>&) method and the
* ServiceObjects::UngetService(const ServiceReferenceBase&) method behaves the
* same as the UngetService(const ServiceReferenceBase&) method. That is, only one,
* use-counted service object is available from the ServiceObjects object.
*
* @tparam S Type of Service.
* @param reference A reference to the service.
* @return A ServiceObjects object for the service associated with the specified
* reference or an invalid instance if the service is not registered.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws std::invalid_argument If the specified ServiceReference is invalid
* (default constructed or the service has been unregistered)
*
* @see PrototypeServiceFactory
*/
template<class S>
ServiceObjects<S> GetServiceObjects(const ServiceReference<S>& reference)
{
return ServiceObjects<S>(this, reference);
}
/**
* Releases the service object referenced by the specified
* <code>ServiceReference</code> object. If the context module's use count
* for the service is zero, this method returns <code>false</code>.
* Otherwise, the context modules's use count for the service is decremented
* by one.
*
* <p>
* The service's service object should no longer be used and all references
* to it should be destroyed when a module's use count for the service drops
* to zero.
*
* <p>
* The following steps are taken to unget the service object:
* <ol>
* <li>If the context module's use count for the service is zero or the
* service has been unregistered, <code>false</code> is returned.
* <li>The context module's use count for this service is decremented by
* one.
* <li>If the context module's use count for the service is currently zero
* and the service was registered with a <code>ServiceFactory</code> object,
* the ServiceFactory#UngetService
* method is called to release the service object for the context module.
* <li><code>true</code> is returned.
* </ol>
*
* @param reference A reference to the service to be released.
* @return <code>false</code> if the context module's use count for the
* service is zero or if the service has been unregistered;
* <code>true</code> otherwise.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see #GetService
* @see ServiceFactory
*/
bool UngetService(const ServiceReferenceBase& reference);
void AddServiceListener(const ServiceListener& delegate,
const std::string& filter = std::string());
void RemoveServiceListener(const ServiceListener& delegate);
void AddModuleListener(const ModuleListener& delegate);
void RemoveModuleListener(const ModuleListener& delegate);
/**
* Adds the specified <code>callback</code> with the
* specified <code>filter</code> to the context modules's list of listeners.
* See LDAPFilter for a description of the filter syntax. Listeners
* are notified when a service has a lifecycle state change.
*
* <p>
- * You must take care to remove registered listeners befor the <code>receiver</code>
+ * You must take care to remove registered listeners before the <code>receiver</code>
* object is destroyed. However, the Micro Services framework takes care
* of removing all listeners registered by this context module's classes
* after the module is unloaded.
*
* <p>
* If the context module's list of listeners already contains a pair <code>(r,c)</code>
* of <code>receiver</code> and <code>callback</code> such that
* <code>(r == receiver && c == callback)</code>, then this
* method replaces that callback's filter (which may be empty)
* with the specified one (which may be empty).
*
* <p>
* The callback is called if the filter criteria is met. To filter based
* upon the class of the service, the filter should reference the
* us::ServiceConstants::OBJECTCLASS() property. If <code>filter</code> is
* empty, all services are considered to match the filter.
*
* <p>
* When using a <code>filter</code>, it is possible that the
* <code>ServiceEvent</code>s for the complete lifecycle of a service
* will not be delivered to the callback. For example, if the
* <code>filter</code> only matches when the property <code>x</code> has
* the value <code>1</code>, the callback will not be called if the
* service is registered with the property <code>x</code> not set to the
* value <code>1</code>. Subsequently, when the service is modified
* setting property <code>x</code> to the value <code>1</code>, the
* filter will match and the callback will be called with a
* <code>ServiceEvent</code> of type <code>MODIFIED</code>. Thus, the
* callback will not be called with a <code>ServiceEvent</code> of type
* <code>REGISTERED</code>.
*
* @tparam R The type of the receiver (containing the member function to be called)
* @param receiver The object to connect to.
* @param callback The member function pointer to call.
* @param filter The filter criteria.
* @throws std::invalid_argument If <code>filter</code> contains an
* invalid filter string that cannot be parsed.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see ServiceEvent
* @see RemoveServiceListener()
*/
template<class R>
void AddServiceListener(R* receiver, void(R::*callback)(const ServiceEvent),
const std::string& filter = std::string())
{
AddServiceListener(ServiceListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver), filter);
}
/**
* Removes the specified <code>callback</code> from the context module's
* list of listeners.
*
* <p>
* If the <code>(receiver,callback)</code> pair is not contained in this
* context module's list of listeners, this method does nothing.
*
* @tparam R The type of the receiver (containing the member function to be removed)
* @param receiver The object from which to disconnect.
* @param callback The member function pointer to remove.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see AddServiceListener()
*/
template<class R>
void RemoveServiceListener(R* receiver, void(R::*callback)(const ServiceEvent))
{
RemoveServiceListener(ServiceListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver));
}
/**
* Adds the specified <code>callback</code> to the context modules's list
* of listeners. Listeners are notified when a module has a lifecycle
* state change.
*
* <p>
* If the context module's list of listeners already contains a pair <code>(r,c)</code>
* of <code>receiver</code> and <code>callback</code> such that
* <code>(r == receiver && c == callback)</code>, then this method does nothing.
*
* @tparam R The type of the receiver (containing the member function to be called)
* @param receiver The object to connect to.
* @param callback The member function pointer to call.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see ModuleEvent
*/
template<class R>
void AddModuleListener(R* receiver, void(R::*callback)(const ModuleEvent))
{
AddModuleListener(ModuleListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver));
}
/**
* Removes the specified <code>callback</code> from the context module's
* list of listeners.
*
* <p>
* If the <code>(receiver,callback)</code> pair is not contained in this
* context module's list of listeners, this method does nothing.
*
* @tparam R The type of the receiver (containing the member function to be removed)
* @param receiver The object from which to disconnect.
* @param callback The member function pointer to remove.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see AddModuleListener()
*/
template<class R>
void RemoveModuleListener(R* receiver, void(R::*callback)(const ModuleEvent))
{
RemoveModuleListener(ModuleListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver));
}
/**
* Get the absolute path for a file or directory in the persistent
* storage area provided for the module. The returned path
* might be empty if no storage path has been set previously.
* If the path is non-empty, it is safe to assume that the path is writable.
*
* @see ModuleSettings::SetStoragePath(const std::string&)
*
* @param filename A relative name to the file or directory to be accessed.
* @return The absolute path to the persistent storage area for the given file name.
*/
std::string GetDataFile(const std::string& filename) const;
private:
friend class Module;
friend class ModulePrivate;
ModuleContext(ModulePrivate* module);
// purposely not implemented
ModuleContext(const ModuleContext&);
ModuleContext& operator=(const ModuleContext&);
void AddServiceListener(const ServiceListener& delegate, void* data,
const std::string& filter);
void RemoveServiceListener(const ServiceListener& delegate, void* data);
void AddModuleListener(const ModuleListener& delegate, void* data);
void RemoveModuleListener(const ModuleListener& delegate, void* data);
ModuleContextPrivate * const d;
};
US_END_NAMESPACE
#endif /* USMODULECONTEXT_H_ */
diff --git a/Modules/CppMicroServices/core/include/usModuleEvent.h b/Modules/CppMicroServices/core/include/usModuleEvent.h
index 8a745018f3..3fc13eee38 100644
--- a/Modules/CppMicroServices/core/include/usModuleEvent.h
+++ b/Modules/CppMicroServices/core/include/usModuleEvent.h
@@ -1,153 +1,153 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USMODULEEVENT_H
#define USMODULEEVENT_H
#include <iostream>
#include "usSharedData.h"
US_BEGIN_NAMESPACE
class Module;
class ModuleEventData;
/**
* \ingroup MicroServices
*
* An event from the Micro Services framework describing a module lifecycle change.
* <p>
* <code>ModuleEvent</code> objects are delivered to listeners connected
* via ModuleContext::AddModuleListener() when a change
* occurs in a modules's lifecycle. A type code is used to identify
* the event type for future extendability.
*
* @see ModuleContext#AddModuleListener
*/
class US_Core_EXPORT ModuleEvent
{
SharedDataPointer<ModuleEventData> d;
public:
enum Type {
/**
* The module has been loaded.
* <p>
* The module's
* \link ModuleActivator::Load(ModuleContext*) ModuleActivator Load\endlink method
* has been executed.
*/
LOADED,
/**
* The module has been unloaded.
* <p>
* The module's
* \link ModuleActivator::Unload(ModuleContext*) ModuleActivator Unload\endlink method
* has been executed.
*/
UNLOADED,
/**
* The module is about to be loaded.
* <p>
* The module's
* \link ModuleActivator::Load(ModuleContext*) ModuleActivator Load\endlink method
* is about to be called.
*/
LOADING,
/**
* The module is about to be unloaded.
* <p>
* The module's
* \link ModuleActivator::Unload(ModuleContext*) ModuleActivator Unload\endlink method
* is about to be called.
*/
UNLOADING
};
/**
* Creates an invalid instance.
*/
ModuleEvent();
~ModuleEvent();
/**
* Can be used to check if this ModuleEvent instance is valid,
* or if it has been constructed using the default constructor.
*
* @return <code>true</code> if this event object is valid,
* <code>false</code> otherwise.
*/
bool IsNull() const;
/**
* Creates a module event of the specified type.
*
* @param type The event type.
* @param module The module which had a lifecycle change.
*/
ModuleEvent(Type type, Module* module);
ModuleEvent(const ModuleEvent& other);
ModuleEvent& operator=(const ModuleEvent& other);
/**
* Returns the module which had a lifecycle change.
*
* @return The module that had a change occur in its lifecycle.
*/
Module* GetModule() const;
/**
- * Returns the type of lifecyle event. The type values are:
+ * Returns the type of lifecycle event. The type values are:
* <ul>
* <li>{@link #LOADING}
* <li>{@link #LOADED}
* <li>{@link #UNLOADING}
* <li>{@link #UNLOADED}
* </ul>
*
* @return The type of lifecycle event.
*/
Type GetType() const;
};
/**
* \ingroup MicroServices
* @{
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, ModuleEvent::Type eventType);
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ModuleEvent& event);
/** @}*/
US_END_NAMESPACE
#endif // USMODULEEVENT_H
diff --git a/Modules/CppMicroServices/core/include/usModuleResource.h b/Modules/CppMicroServices/core/include/usModuleResource.h
index fa06df61e1..3c068aadae 100644
--- a/Modules/CppMicroServices/core/include/usModuleResource.h
+++ b/Modules/CppMicroServices/core/include/usModuleResource.h
@@ -1,312 +1,312 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USMODULERESOURCE_H
#define USMODULERESOURCE_H
#include <usCoreExport.h>
#include <ostream>
#include <vector>
US_MSVC_PUSH_DISABLE_WARNING(4396)
US_BEGIN_NAMESPACE
class ModuleResourcePrivate;
class ModuleResourceContainer;
/**
* \ingroup MicroServices
*
* Represents a resource (text file, image, etc.) embedded in a CppMicroServices module.
*
* A \c %ModuleResource object provides information about a resource (external file) which
* was embedded into this module's shared library. \c %ModuleResource objects can be obtained
* be calling Module#GetResource or Module#FindResources.
*
- * Example code for retreiving a resource object and reading its contents:
+ * Example code for retrieving a resource object and reading its contents:
* \snippet uServices-resources/main.cpp 1
*
* %ModuleResource objects have value semantics and copies are very inexpensive.
*
* \see ModuleResourceStream
* \see \ref MicroServices_Resources
*/
class US_Core_EXPORT ModuleResource
{
private:
typedef ModuleResourcePrivate* ModuleResource::*bool_type;
public:
/**
* Creates in invalid %ModuleResource object.
*/
ModuleResource();
/**
* Copy constructor.
* @param resource The object to be copied.
*/
ModuleResource(const ModuleResource& resource);
~ModuleResource();
/**
* Assignment operator.
*
* @param resource The %ModuleResource object which is assigned to this instance.
* @return A reference to this %ModuleResource instance.
*/
ModuleResource& operator=(const ModuleResource& resource);
/**
* A less then operator using the full resource path as returned by
* GetResourcePath() to define the ordering.
*
* @param resource The object to which this %ModuleResource object is compared to.
* @return \c true if this %ModuleResource object is less then \c resource,
* \c false otherwise.
*/
bool operator<(const ModuleResource& resource) const;
/**
* Equality operator for %ModuleResource objects.
*
* @param resource The object for testing equality.
* @return \c true if this %ModuleResource object is equal to \c resource, i.e.
* they are coming from the same module (shared or static) and have an equal
* resource path, \c false otherwise.
*/
bool operator==(const ModuleResource& resource) const;
/**
* Inequality operator for %ModuleResource objects.
*
* @param resource The object for testing inequality.
* @return The result of <code>!(*this == resource)</code>.
*/
bool operator!=(const ModuleResource& resource) const;
/**
* Tests this %ModuleResource object for validity.
*
* Invalid %ModuleResource objects are created by the default constructor or
* can be returned by the Module class if the resource path is not found.
*
* @return \c true if this %ModuleReource object is valid and can safely be used,
* \c false otherwise.
*/
bool IsValid() const;
/**
* Boolean conversion operator using IsValid().
*/
operator bool_type() const;
/**
* Returns the name of the resource, excluding the path.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string name = resource.GetName(); // name = "archive.tar.gz"
* \endcode
*
* @return The resource name.
* @see GetPath(), GetResourcePath()
*/
std::string GetName() const;
/**
* Returns the resource's path, without the file name.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string path = resource.GetPath(); // path = "/data/"
* \endcode
*
* The path with always begin and end with a forward slash.
*
* @return The resource path without the name.
* @see GetResourcePath(), GetName() and IsDir()
*/
std::string GetPath() const;
/**
* Returns the resource path including the file name.
*
* @return The resource path including the file name.
* @see GetPath(), GetName() and IsDir()
*/
std::string GetResourcePath() const;
/**
* Returns the base name of the resource without the path.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string base = resource.GetBaseName(); // base = "archive"
* \endcode
*
* @return The resource base name.
* @see GetName(), GetSuffix(), GetCompleteSuffix() and GetCompleteBaseName()
*/
std::string GetBaseName() const;
/**
* Returns the complete base name of the resource without the path.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string base = resource.GetCompleteBaseName(); // base = "archive.tar"
* \endcode
*
* @return The resource's complete base name.
* @see GetName(), GetSuffix(), GetCompleteSuffix(), and GetBaseName()
*/
std::string GetCompleteBaseName() const;
/**
* Returns the suffix of the resource.
*
* The suffix consists of all characters in the resource name after (but not
* including) the last '.'.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string suffix = resource.GetSuffix(); // suffix = "gz"
* \endcode
*
* @return The resource name suffix.
* @see GetName(), GetCompleteSuffix(), GetBaseName() and GetCompleteBaseName()
*/
std::string GetSuffix() const;
/**
* Returns the complete suffix of the resource.
*
* The suffix consists of all characters in the resource name after (but not
* including) the first '.'.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string suffix = resource.GetCompleteSuffix(); // suffix = "tar.gz"
* \endcode
*
* @return The resource name suffix.
* @see GetName(), GetSuffix(), GetBaseName(), and GetCompleteBaseName()
*/
std::string GetCompleteSuffix() const;
/**
* Returns \c true if this %ModuleResource object points to a directory and thus
* may have child resources.
*
* @return \c true if this object points to a directory, \c false otherwise.
*/
bool IsDir() const;
/**
* Returns \c true if this %ModuleResource object points to a file resource.
*
* @return \c true if this object points to an embedded file, \c false otherwise.
*/
bool IsFile() const;
/**
* Returns a list of resource names which are children of this object.
*
* The returned names are relative to the path of this %ModuleResource object
* and may contain file as well as directory entries.
*
* @return A list of child resource names.
*/
std::vector<std::string> GetChildren() const;
/**
* Returns a list of resource objects which are children of this object.
*
* The return ModuleResource objects may contain files as well as
* directory resources.
*
* @return A list of child resource objects.
*/
std::vector<ModuleResource> GetChildResources() const;
/**
* Returns the size of the resource data for this %ModuleResource object.
*
* @return The resource data size.
*/
int GetSize() const;
/**
* Returns the last modified time of this resource in seconds from the epoch.
*
* @return Last modified time of this resource.
*/
time_t GetLastModified() const;
private:
ModuleResource(const std::string& file, const ModuleResourceContainer& resourceContainer);
ModuleResource(int index, const ModuleResourceContainer& resourceContainer);
friend class Module;
friend class ModulePrivate;
friend class ModuleResourceContainer;
friend class ModuleResourceStream;
US_HASH_FUNCTION_FRIEND(ModuleResource);
std::size_t Hash() const;
void* GetData() const;
ModuleResourcePrivate* d;
};
US_END_NAMESPACE
US_MSVC_POP_WARNING
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ModuleResource)& resource);
US_HASH_FUNCTION_NAMESPACE_BEGIN
US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ModuleResource))
return arg.Hash();
US_HASH_FUNCTION_END
US_HASH_FUNCTION_NAMESPACE_END
#endif // USMODULERESOURCE_H
diff --git a/Modules/CppMicroServices/core/include/usModuleSettings.h b/Modules/CppMicroServices/core/include/usModuleSettings.h
index 7f1c17ee1a..9646c51bc7 100644
--- a/Modules/CppMicroServices/core/include/usModuleSettings.h
+++ b/Modules/CppMicroServices/core/include/usModuleSettings.h
@@ -1,171 +1,171 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USMODULESETTINGS_H
#define USMODULESETTINGS_H
#include "usCoreConfig.h"
#include <vector>
#include <string>
US_BEGIN_NAMESPACE
/**
* \ingroup MicroServices
*
* Query and set certain properties of the CppMicroServices library.
*
* The following environment variables influence the runtime behavior
* of the CppMicroServices library:
*
* - \e US_DISABLE_AUTOLOADING If set, auto-loading of modules is disabled.
* - \e US_AUTOLOAD_PATHS A ':' (Unix) or ';' (Windows) separated list of paths
* from which modules should be auto-loaded.
*
* \remarks This class is thread safe.
*/
class US_Core_EXPORT ModuleSettings
{
public:
typedef std::vector<std::string> PathList;
/**
* Returns a special string which can be used as an argument for a
* AddAutoLoadPath() call.
*
* When a module is loaded and this string has been added as a path
* to the list of auto-load paths the CppMicroServices library will
* auto-load all modules from the currently being loaded module's
* auto-load directory.
*
* \return A string to be used in AddAutoLoadPath().
*
* \remarks The returned string is contained in the default set of
* auto-load paths, unless a new set of paths is given by a call to
* SetAutoLoadPaths().
*
* \sa MicroServices_AutoLoading
* \sa US_INITIALIZE_MODULE
*/
static std::string CURRENT_MODULE_PATH();
/**
* \return \c true if threading support has been configured into the
* CppMicroServices library, \c false otherwise.
*/
static bool IsThreadingSupportEnabled();
/**
* \return \c true if support for module auto-loading is enabled,
* \c false otherwise.
*
* \remarks This method will always return \c false if support for auto-loading
* has not been configured into the CppMicroServices library or if it has been
* disabled by defining the US_DISABLE_AUTOLOADING environment variable.
*/
static bool IsAutoLoadingEnabled();
/**
* Enable or disable auto-loading support.
*
* \param enable If \c true, enable auto-loading support, disable it otherwise.
*
* \remarks Calling this method will have no effect if support for
* auto-loading has not been configured into the CppMicroServices library of it
- * it has been disabled by defining the US_DISABLE_AUTOLOADING envrionment variable.
+ * it has been disabled by defining the US_DISABLE_AUTOLOADING environment variable.
*/
static void SetAutoLoadingEnabled(bool enable);
/**
* \return A list of paths in the file-system from which modules will be
* auto-loaded.
*/
static PathList GetAutoLoadPaths();
/**
* Set a list of paths in the file-system from which modules should be
* auto-loaded.
* @param paths A list of absolute file-system paths.
*/
static void SetAutoLoadPaths(const PathList& paths);
/**
* Add a path in the file-system to the list of paths from which modules
* will be auto-loaded.
*
* @param path The additional absolute auto-load path in the file-system.
*/
static void AddAutoLoadPath(const std::string& path);
/**
* Set a local storage path for persistend module data.
*
* This path is used as a base directory for providing modules
* with a storage path for writing persistent data. The callee
* must ensure that the provided path exists and is writable.
*
* @see ModuleContext::GetDataFile(const std::string&)
*
* @param path An absolute path for writing persistent data.
*/
static void SetStoragePath(const std::string& path);
/**
* Get the absolute path for persistent data. The returned path
* might be empty. If the path is non-empty, it is safe to assume
* that the path exists and is writable.
*
* @return The absolute path to the persistent storage path.
*/
static std::string GetStoragePath();
/**
* Set the logging level for log messages from CppMicroServices modules.
*
* Higher logging levels will discard messages with lower priority.
* E.g. a logging level of WarningMsg will discard all messages of
* type DebugMsg and InfoMsg.
*
* @param level The new logging level.
*/
static void SetLogLevel(MsgType level);
/**
* Get the current logging level.
*
* @return The currently used logging level.
*/
static MsgType GetLogLevel();
private:
// purposely not implemented
ModuleSettings();
ModuleSettings(const ModuleSettings&);
ModuleSettings& operator=(const ModuleSettings&);
};
US_END_NAMESPACE
#endif // USMODULESETTINGS_H
diff --git a/Modules/CppMicroServices/core/include/usModuleVersion.h b/Modules/CppMicroServices/core/include/usModuleVersion.h
index 88b8fcb48d..ba08b08b6c 100644
--- a/Modules/CppMicroServices/core/include/usModuleVersion.h
+++ b/Modules/CppMicroServices/core/include/usModuleVersion.h
@@ -1,254 +1,254 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USMODULEVERSION_H
#define USMODULEVERSION_H
#include <usCoreExport.h>
#include <string>
US_BEGIN_NAMESPACE
/**
* \ingroup MicroServices
*
* Version identifier for CppMicroServices modules.
*
* <p>
* Version identifiers have four components.
* <ol>
* <li>Major version. A non-negative integer.</li>
* <li>Minor version. A non-negative integer.</li>
* <li>Micro version. A non-negative integer.</li>
* <li>Qualifier. A text string. See <code>ModuleVersion(const std::string&)</code> for the
* format of the qualifier string.</li>
* </ol>
*
* <p>
* <code>ModuleVersion</code> objects are immutable.
*/
class US_Core_EXPORT ModuleVersion {
private:
friend class ModulePrivate;
unsigned int majorVersion;
unsigned int minorVersion;
unsigned int microVersion;
std::string qualifier;
static const char SEPARATOR; // = "."
bool undefined;
/**
* Called by the ModuleVersion constructors to validate the version components.
*
- * @return <code>true</code> if the validation was successfull, <code>false</code> otherwise.
+ * @return <code>true</code> if the validation was successful, <code>false</code> otherwise.
*/
void Validate();
ModuleVersion& operator=(const ModuleVersion& v);
explicit ModuleVersion(bool undefined = false);
public:
/**
* The empty version "0.0.0".
*/
static ModuleVersion EmptyVersion();
/**
* Creates an undefined version identifier, representing either
* infinity or minus infinity.
*/
static ModuleVersion UndefinedVersion();
/**
* Creates a version identifier from the specified numerical components.
*
* <p>
* The qualifier is set to the empty string.
*
* @param majorVersion Major component of the version identifier.
* @param minorVersion Minor component of the version identifier.
* @param microVersion Micro component of the version identifier.
*
*/
ModuleVersion(unsigned int majorVersion, unsigned int minorVersion, unsigned int microVersion);
/**
* Creates a version identifier from the specified components.
*
* @param majorVersion Major component of the version identifier.
* @param minorVersion Minor component of the version identifier.
* @param microVersion Micro component of the version identifier.
* @param qualifier Qualifier component of the version identifier.
*/
ModuleVersion(unsigned int majorVersion, unsigned int minorVersion, unsigned int microVersion, const std::string& qualifier);
/**
* Created a version identifier from the specified string.
*
* <p>
* Here is the grammar for version strings.
*
* <pre>
* version ::= majorVersion('.'minorVersion('.'microVersion('.'qualifier)?)?)?
* majorVersion ::= digit+
* minorVersion ::= digit+
* microVersion ::= digit+
* qualifier ::= (alpha|digit|'_'|'-')+
* digit ::= [0..9]
* alpha ::= [a..zA..Z]
* </pre>
*
* There must be no whitespace in version.
*
* @param version string representation of the version identifier.
*/
ModuleVersion(const std::string& version);
/**
* Create a version identifier from another.
*
* @param version Another version identifier
*/
ModuleVersion(const ModuleVersion& version);
/**
* Parses a version identifier from the specified string.
*
* <p>
* See <code>ModuleVersion(const std::string&)</code> for the format of the version string.
*
* @param version string representation of the version identifier. Leading
* and trailing whitespace will be ignored.
* @return A <code>ModuleVersion</code> object representing the version
* identifier. If <code>version</code> is the empty string
* then <code>EmptyVersion</code> will be
* returned.
*/
static ModuleVersion ParseVersion(const std::string& version);
/**
* Returns the undefined state of this version identifier.
*
* @return <code>true</code> if this version identifier is undefined,
* <code>false</code> otherwise.
*/
bool IsUndefined() const;
/**
* Returns the majorVersion component of this version identifier.
*
* @return The majorVersion component.
*/
unsigned int GetMajor() const;
/**
* Returns the minorVersion component of this version identifier.
*
* @return The minorVersion component.
*/
unsigned int GetMinor() const;
/**
* Returns the microVersion component of this version identifier.
*
* @return The microVersion component.
*/
unsigned int GetMicro() const;
/**
* Returns the qualifier component of this version identifier.
*
* @return The qualifier component.
*/
std::string GetQualifier() const;
/**
* Returns the string representation of this version identifier.
*
* <p>
* The format of the version string will be <code>majorVersion.minorVersion.microVersion</code>
* if qualifier is the empty string or
* <code>majorVersion.minorVersion.microVersion.qualifier</code> otherwise.
*
* @return The string representation of this version identifier.
*/
std::string ToString() const;
/**
* Compares this <code>ModuleVersion</code> object to another object.
*
* <p>
* A version is considered to be <b>equal to </b> another version if the
* majorVersion, minorVersion and microVersion components are equal and the qualifier component
* is equal.
*
* @param object The <code>ModuleVersion</code> object to be compared.
* @return <code>true</code> if <code>object</code> is a
* <code>ModuleVersion</code> and is equal to this object;
* <code>false</code> otherwise.
*/
bool operator==(const ModuleVersion& object) const;
/**
* Compares this <code>ModuleVersion</code> object to another object.
*
* <p>
* A version is considered to be <b>less than </b> another version if its
* majorVersion component is less than the other version's majorVersion component, or the
* majorVersion components are equal and its minorVersion component is less than the other
* version's minorVersion component, or the majorVersion and minorVersion components are equal
* and its microVersion component is less than the other version's microVersion component,
* or the majorVersion, minorVersion and microVersion components are equal and it's qualifier
* component is less than the other version's qualifier component (using
* <code>std::string::operator<()</code>).
*
* <p>
* A version is considered to be <b>equal to</b> another version if the
* majorVersion, minorVersion and microVersion components are equal and the qualifier component
* is equal.
*
* @param object The <code>ModuleVersion</code> object to be compared.
* @return A negative integer, zero, or a positive integer if this object is
* less than, equal to, or greater than the specified
* <code>ModuleVersion</code> object.
*/
int Compare(const ModuleVersion& object) const;
};
US_END_NAMESPACE
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ModuleVersion)& v);
#endif // USMODULEVERSION_H
diff --git a/Modules/CppMicroServices/core/include/usServiceEvent.h b/Modules/CppMicroServices/core/include/usServiceEvent.h
index 399928e5d4..7990af55c6 100644
--- a/Modules/CppMicroServices/core/include/usServiceEvent.h
+++ b/Modules/CppMicroServices/core/include/usServiceEvent.h
@@ -1,188 +1,188 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICEEVENT_H
#define USSERVICEEVENT_H
#ifdef REGISTERED
#ifdef _WIN32
#error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\
enum type. Try to reorder your includes, compile with WIN32_LEAN_AND_MEAN, or undef\
- the REGISTERED macro befor including this header.
+ the REGISTERED macro before including this header.
#else
#error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\
- enum type. Try to reorder your includes or undef the REGISTERED macro befor including\
+ enum type. Try to reorder your includes or undef the REGISTERED macro before including\
this header.
#endif
#endif
#include "usSharedData.h"
#include "usServiceReference.h"
US_BEGIN_NAMESPACE
class ServiceEventData;
/**
* \ingroup MicroServices
*
* An event from the Micro Services framework describing a service lifecycle change.
* <p>
* <code>ServiceEvent</code> objects are delivered to
* listeners connected via ModuleContext::AddServiceListener() when a
* change occurs in this service's lifecycle. A type code is used to identify
* the event type for future extendability.
*/
class US_Core_EXPORT ServiceEvent
{
SharedDataPointer<ServiceEventData> d;
public:
enum Type {
/**
* This service has been registered.
* <p>
* This event is delivered <strong>after</strong> the service
* has been registered with the framework.
*
* @see ModuleContext#RegisterService()
*/
REGISTERED = 0x00000001,
/**
* The properties of a registered service have been modified.
* <p>
* This event is delivered <strong>after</strong> the service
* properties have been modified.
*
* @see ServiceRegistration#SetProperties
*/
MODIFIED = 0x00000002,
/**
* This service is in the process of being unregistered.
* <p>
* This event is delivered <strong>before</strong> the service
* has completed unregistering.
*
* <p>
* If a module is using a service that is <code>UNREGISTERING</code>, the
* module should release its use of the service when it receives this event.
* If the module does not release its use of the service when it receives
* this event, the framework will automatically release the module's use of
* the service while completing the service unregistration operation.
*
* @see ServiceRegistration#Unregister
* @see ModuleContext#UngetService
*/
UNREGISTERING = 0x00000004,
/**
* The properties of a registered service have been modified and the new
* properties no longer match the listener's filter.
* <p>
* This event is delivered <strong>after</strong> the service
* properties have been modified. This event is only delivered to listeners
* which were added with a non-empty filter where the filter
* matched the service properties prior to the modification but the filter
* does not match the modified service properties.
*
* @see ServiceRegistration#SetProperties
*/
MODIFIED_ENDMATCH = 0x00000008
};
/**
* Creates an invalid instance.
*/
ServiceEvent();
~ServiceEvent();
/**
* Can be used to check if this ServiceEvent instance is valid,
* or if it has been constructed using the default constructor.
*
* @return <code>true</code> if this event object is valid,
* <code>false</code> otherwise.
*/
bool IsNull() const;
/**
* Creates a new service event object.
*
* @param type The event type.
* @param reference A <code>ServiceReference</code> object to the service
* that had a lifecycle change.
*/
ServiceEvent(Type type, const ServiceReferenceBase& reference);
ServiceEvent(const ServiceEvent& other);
ServiceEvent& operator=(const ServiceEvent& other);
/**
* Returns a reference to the service that had a change occur in its
* lifecycle.
* <p>
* This reference is the source of the event.
*
* @return Reference to the service that had a lifecycle change.
*/
ServiceReferenceU GetServiceReference() const;
template<class S>
ServiceReference<S> GetServiceReference(InterfaceType<S>) const
{
return GetServiceReference();
}
/**
* Returns the type of event. The event type values are:
* <ul>
* <li>{@link #REGISTERED} </li>
* <li>{@link #MODIFIED} </li>
* <li>{@link #MODIFIED_ENDMATCH} </li>
* <li>{@link #UNREGISTERING} </li>
* </ul>
*
* @return Type of service lifecycle change.
*/
Type GetType() const;
};
/**
* \ingroup MicroServices
* @{
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ServiceEvent::Type& type);
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ServiceEvent& event);
/** @}*/
US_END_NAMESPACE
#endif // USSERVICEEVENT_H
diff --git a/Modules/CppMicroServices/core/include/usServiceInterface.h b/Modules/CppMicroServices/core/include/usServiceInterface.h
index 0c1927374a..a7505d757a 100644
--- a/Modules/CppMicroServices/core/include/usServiceInterface.h
+++ b/Modules/CppMicroServices/core/include/usServiceInterface.h
@@ -1,343 +1,343 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICEINTERFACE_H
#define USSERVICEINTERFACE_H
#include <usGlobalConfig.h>
#include <usServiceException.h>
#include <map>
#include <string>
#include <typeinfo>
US_BEGIN_NAMESPACE
std::string GetDemangledName(const std::type_info& typeInfo);
US_END_NAMESPACE
/**
* \ingroup MicroServices
*
* Returns a unique id for a given type. By default, the
* demangled name of \c T is returned.
*
* This template method may be specialized directly or be
* using the macro #US_DECLARE_SERVICE_INTERFACE to return
* a custom id for each service interface.
*
* @tparam T The service interface type.
* @return A unique id for the service interface type T.
*/
template<class T> std::string us_service_interface_iid()
{
return US_PREPEND_NAMESPACE(GetDemangledName)(typeid(T));
}
/// \cond
template<> inline std::string us_service_interface_iid<void>() { return std::string(); }
/// \endcond
/**
* \ingroup MicroServices
*
* \brief Declare a service interface id.
*
* This macro associates the given identifier \e _service_interface_id (a string literal) to the
* interface class called _service_interface_type. The Identifier must be unique. For example:
*
* \code
* #include <usServiceInterface.h>
*
* struct ISomeInterace { ... };
*
* US_DECLARE_SERVICE_INTERFACE(ISomeInterface, "com.mycompany.service.ISomeInterface/1.0")
* \endcode
*
* The usage of this macro is optional and the service interface id which is automatically
* associated with any type is usually good enough (the demangled type name). However, care must
* be taken if the default id is compared with a string literal hard-coding a service interface
* id. E.g. the default id for templated types in the STL may differ between platforms. For
* user-defined types and templates the ids are typically consistent, but platform specific
* default template arguments will lead to different ids.
*
* This macro is normally used right after the class definition for _service_interface_type,
* in a header file.
*
* If you want to use #US_DECLARE_SERVICE_INTERFACE with interface classes declared in a
* namespace then you have to make sure the #US_DECLARE_SERVICE_INTERFACE macro call is not
* inside a namespace though. For example:
*
* \code
* #include <usServiceInterface.h>
*
* namespace Foo
* {
* struct ISomeInterface { ... };
* }
*
* US_DECLARE_SERVICE_INTERFACE(Foo::ISomeInterface, "com.mycompany.service.ISomeInterface/1.0")
* \endcode
*
* @param _service_interface_type The service interface type.
* @param _service_interface_id A string literal representing a globally unique identifier.
*/
#define US_DECLARE_SERVICE_INTERFACE(_service_interface_type, _service_interface_id) \
template<> inline std::string us_service_interface_iid<_service_interface_type>() \
{ return _service_interface_id; } \
US_BEGIN_NAMESPACE
class ServiceFactory;
/**
* @ingroup MicroServices
*
* A helper type used in several methods to get proper
* method overload resolutions.
*/
template<class Interface>
struct InterfaceType {};
/**
* @ingroup MicroServices
*
* A map containing interfaces ids and their corresponding service object
* pointers. InterfaceMap instances represent a complete service object
- * which implementes one or more service interfaces. For each implemented
+ * which implements one or more service interfaces. For each implemented
* service interface, there is an entry in the map with the key being
* the service interface id and the value a pointer to the service
* interface implementation.
*
* To create InterfaceMap instances, use the MakeInterfaceMap helper class.
*
* @note This is a low-level type and should only rarely be used.
*
* @see MakeInterfaceMap
*/
typedef std::map<std::string, void*> InterfaceMap;
template<class I>
bool InsertInterfaceType(InterfaceMap& im, I* i)
{
if (us_service_interface_iid<I>().empty())
{
throw ServiceException(std::string("The interface class ") + typeid(I).name() +
" uses an invalid id in its US_DECLARE_SERVICE_INTERFACE macro call.");
}
im.insert(std::make_pair(std::string(us_service_interface_iid<I>()),
static_cast<void*>(static_cast<I*>(i))));
return true;
}
template<>
inline bool InsertInterfaceType<void>(InterfaceMap&, void*)
{
return false;
}
/**
* @ingroup MicroServices
*
* Helper class for constructing InterfaceMap instances based
* on service implementations or service factories.
*
* Example usage:
* \code
- * MyService service; // implementes I1 and I2
+ * MyService service; // implements I1 and I2
* InterfaceMap im = MakeInterfaceMap<I1,I2>(&service);
* \endcode
*
* The MakeInterfaceMap supports service implementations with
* up to three service interfaces.
*
* @see InterfaceMap
*/
template<class I1, class I2 = void, class I3 = void>
struct MakeInterfaceMap
{
ServiceFactory* m_factory;
I1* m_interface1;
I2* m_interface2;
I3* m_interface3;
/**
* Constructor taking a service implementation pointer.
*
* @param impl A service implementation pointer, which must
* be castable to a all specified service interfaces.
*/
template<class Impl>
MakeInterfaceMap(Impl* impl)
: m_factory(nullptr)
, m_interface1(static_cast<I1*>(impl))
, m_interface2(static_cast<I2*>(impl))
, m_interface3(static_cast<I3*>(impl))
{}
/**
* Constructor taking a service factory.
*
* @param factory A service factory.
*/
MakeInterfaceMap(ServiceFactory* factory)
: m_factory(factory)
, m_interface1(nullptr)
, m_interface2(nullptr)
, m_interface3(nullptr)
{
if (factory == nullptr)
{
throw ServiceException("The service factory argument must not be nullptr.");
}
}
operator InterfaceMap ()
{
InterfaceMap sim;
InsertInterfaceType(sim, m_interface1);
InsertInterfaceType(sim, m_interface2);
InsertInterfaceType(sim, m_interface3);
if (m_factory)
{
sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"),
static_cast<void*>(m_factory)));
}
return sim;
}
};
/// \cond
template<class I1, class I2>
struct MakeInterfaceMap<I1,I2,void>
{
ServiceFactory* m_factory;
I1* m_interface1;
I2* m_interface2;
template<class Impl>
MakeInterfaceMap(Impl* impl)
: m_factory(nullptr)
, m_interface1(static_cast<I1*>(impl))
, m_interface2(static_cast<I2*>(impl))
{}
MakeInterfaceMap(ServiceFactory* factory)
: m_factory(factory)
, m_interface1(nullptr)
, m_interface2(nullptr)
{
if (factory == nullptr)
{
throw ServiceException("The service factory argument must not be nullptr.");
}
}
operator InterfaceMap ()
{
InterfaceMap sim;
InsertInterfaceType(sim, m_interface1);
InsertInterfaceType(sim, m_interface2);
if (m_factory)
{
sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"),
static_cast<void*>(m_factory)));
}
return sim;
}
};
template<class I1>
struct MakeInterfaceMap<I1,void,void>
{
ServiceFactory* m_factory;
I1* m_interface1;
template<class Impl>
MakeInterfaceMap(Impl* impl)
: m_factory(nullptr)
, m_interface1(static_cast<I1*>(impl))
{}
MakeInterfaceMap(ServiceFactory* factory)
: m_factory(factory)
, m_interface1(nullptr)
{
if (factory == nullptr)
{
throw ServiceException("The service factory argument must not be nullptr.");
}
}
operator InterfaceMap ()
{
InterfaceMap sim;
InsertInterfaceType(sim, m_interface1);
if (m_factory)
{
sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"),
static_cast<void*>(m_factory)));
}
return sim;
}
};
template<>
struct MakeInterfaceMap<void,void,void>;
/// \endcond
/**
* @ingroup MicroServices
*
* Extract a service interface pointer from a given InterfaceMap instance.
*
* @param map a InterfaceMap instance.
* @return The service interface pointer for the service interface id of the
* \c I1 interface type or nullptr if \c map does not contain an entry
* for the given type.
*
* @see MakeInterfaceMap
*/
template<class I1>
I1* ExtractInterface(const InterfaceMap& map)
{
InterfaceMap::const_iterator iter = map.find(us_service_interface_iid<I1>());
if (iter != map.end())
{
return reinterpret_cast<I1*>(iter->second);
}
return nullptr;
}
US_END_NAMESPACE
#endif // USSERVICEINTERFACE_H
diff --git a/Modules/CppMicroServices/core/include/usServiceReferenceBase.h b/Modules/CppMicroServices/core/include/usServiceReferenceBase.h
index 7ce3d84636..05c3a062e2 100644
--- a/Modules/CppMicroServices/core/include/usServiceReferenceBase.h
+++ b/Modules/CppMicroServices/core/include/usServiceReferenceBase.h
@@ -1,234 +1,234 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICEREFERENCEBASE_H
#define USSERVICEREFERENCEBASE_H
#include <usAny.h>
US_MSVC_PUSH_DISABLE_WARNING(4396)
US_BEGIN_NAMESPACE
class Module;
class ServiceRegistrationBasePrivate;
class ServiceReferenceBasePrivate;
/**
* \ingroup MicroServices
*
* A reference to a service.
*
* \note This class is provided as public API for low-level service queries only.
* In almost all cases you should use the template ServiceReference instead.
*/
class US_Core_EXPORT ServiceReferenceBase {
private:
typedef ServiceReferenceBasePrivate* ServiceReferenceBase::*bool_type;
public:
ServiceReferenceBase(const ServiceReferenceBase& ref);
/**
* Converts this ServiceReferenceBase instance into a boolean
* expression. If this instance was default constructed or
* the service it references has been unregistered, the conversion
* returns <code>false</code>, otherwise it returns <code>true</code>.
*/
operator bool_type() const;
/**
* Releases any resources held or locked by this
* <code>ServiceReferenceBase</code> and renders it invalid.
*/
ServiceReferenceBase& operator=(int null);
~ServiceReferenceBase();
/**
* Returns the property value to which the specified property key is mapped
* in the properties <code>ServiceProperties</code> object of the service
* referenced by this <code>ServiceReferenceBase</code> object.
*
* <p>
* Property keys are case-insensitive.
*
* <p>
* This method continues to return property values after the service has
* been unregistered. This is so references to unregistered services can
* still be interrogated.
*
* @param key The property key.
* @return The property value to which the key is mapped; an invalid Any
* if there is no property named after the key.
*/
Any GetProperty(const std::string& key) const;
/**
* Returns a list of the keys in the <code>ServiceProperties</code>
* object of the service referenced by this <code>ServiceReferenceBase</code>
* object.
*
* <p>
* This method will continue to return the keys after the service has been
* unregistered. This is so references to unregistered services can
* still be interrogated.
*
* @param keys A vector being filled with the property keys.
*/
void GetPropertyKeys(std::vector<std::string>& keys) const;
/**
* Returns the module that registered the service referenced by this
* <code>ServiceReferenceBase</code> object.
*
* <p>
* This method must return <code>0</code> when the service has been
* unregistered. This can be used to determine if the service has been
* unregistered.
*
* @return The module that registered the service referenced by this
* <code>ServiceReferenceBase</code> object; <code>0</code> if that
* service has already been unregistered.
* @see ModuleContext::RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
Module* GetModule() const;
/**
* Returns the modules that are using the service referenced by this
* <code>ServiceReferenceBase</code> object. Specifically, this method returns
* the modules whose usage count for that service is greater than zero.
*
* @param modules A list of modules whose usage count for the service referenced
* by this <code>ServiceReferenceBase</code> object is greater than
* zero.
*/
void GetUsingModules(std::vector<Module*>& modules) const;
/**
* Returns the interface identifier this ServiceReferenceBase object
* is bound to.
*
* A default constructed ServiceReferenceBase object is not bound to
* any interface identifier and calling this method will return an
* empty string.
*
* @return The interface identifier for this ServiceReferenceBase object.
*/
std::string GetInterfaceId() const;
/**
- * Checks wether this ServiceReferenceBase object can be converted to
+ * Checks whether this ServiceReferenceBase object can be converted to
* another ServiceReferenceBase object, which will be bound to the
* given interface identifier.
*
* ServiceReferenceBase objects can be converted if the underlying service
* implementation was registered under multiple service interfaces.
*
* @param interfaceid
* @return \c true if this ServiceReferenceBase object can be converted,
* \c false otherwise.
*/
bool IsConvertibleTo(const std::string& interfaceid) const;
/**
* Compares this <code>ServiceReferenceBase</code> with the specified
* <code>ServiceReferenceBase</code> for order.
*
* <p>
* If this <code>ServiceReferenceBase</code> and the specified
* <code>ServiceReferenceBase</code> have the same \link ServiceConstants::SERVICE_ID()
* service id\endlink they are equal. This <code>ServiceReferenceBase</code> is less
* than the specified <code>ServiceReferenceBase</code> if it has a lower
* {@link ServiceConstants::SERVICE_RANKING service ranking} and greater if it has a
* higher service ranking. Otherwise, if this <code>ServiceReferenceBase</code>
* and the specified <code>ServiceReferenceBase</code> have the same
* {@link ServiceConstants::SERVICE_RANKING service ranking}, this
* <code>ServiceReferenceBase</code> is less than the specified
* <code>ServiceReferenceBase</code> if it has a higher
* {@link ServiceConstants::SERVICE_ID service id} and greater if it has a lower
* service id.
*
* @param reference The <code>ServiceReferenceBase</code> to be compared.
* @return Returns a false or true if this
* <code>ServiceReferenceBase</code> is less than or greater
* than the specified <code>ServiceReferenceBase</code>.
*/
bool operator<(const ServiceReferenceBase& reference) const;
bool operator==(const ServiceReferenceBase& reference) const;
ServiceReferenceBase& operator=(const ServiceReferenceBase& reference);
private:
friend class ModulePrivate;
friend class ModuleContext;
friend class ModuleHooks;
friend class ServiceHooks;
friend class ServiceObjectsBase;
friend class ServiceObjectsBasePrivate;
friend class ServiceRegistrationBase;
friend class ServiceRegistrationBasePrivate;
friend class ServiceListeners;
friend class ServiceRegistry;
friend class LDAPFilter;
template<class S> friend class ServiceReference;
US_HASH_FUNCTION_FRIEND(ServiceReferenceBase);
std::size_t Hash() const;
/**
* Creates an invalid ServiceReferenceBase object. You can use
* this object in boolean expressions and it will evaluate to
* <code>false</code>.
*/
ServiceReferenceBase();
ServiceReferenceBase(ServiceRegistrationBasePrivate* reg);
void SetInterfaceId(const std::string& interfaceId);
ServiceReferenceBasePrivate* d;
};
US_END_NAMESPACE
US_MSVC_POP_WARNING
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ServiceReferenceBase)& serviceRef);
US_HASH_FUNCTION_NAMESPACE_BEGIN
US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ServiceReferenceBase))
return arg.Hash();
US_HASH_FUNCTION_END
US_HASH_FUNCTION_NAMESPACE_END
#endif // USSERVICEREFERENCEBASE_H
diff --git a/Modules/CppMicroServices/core/include/usServiceRegistration.h b/Modules/CppMicroServices/core/include/usServiceRegistration.h
index 8c925bd588..a9c8acc6d3 100644
--- a/Modules/CppMicroServices/core/include/usServiceRegistration.h
+++ b/Modules/CppMicroServices/core/include/usServiceRegistration.h
@@ -1,203 +1,203 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICEREGISTRATION_H
#define USSERVICEREGISTRATION_H
#include "usServiceRegistrationBase.h"
US_BEGIN_NAMESPACE
/**
* \ingroup MicroServices
*
* A registered service.
*
* <p>
* The framework returns a <code>ServiceRegistration</code> object when a
* <code>ModuleContext#RegisterService()</code> method invocation is successful.
* The <code>ServiceRegistration</code> object is for the private use of the
* registering module and should not be shared with other modules.
* <p>
* The <code>ServiceRegistration</code> object may be used to update the
* properties of the service or to unregister the service.
*
- * @tparam S Class tyoe of the service interface
+ * @tparam S Class type of the service interface
* @see ModuleContext#RegisterService()
* @remarks This class is thread safe.
*/
template<class I1, class I2 = void, class I3 = void>
class ServiceRegistration : public ServiceRegistrationBase
{
public:
/**
* Creates an invalid ServiceRegistration object. You can use
* this object in boolean expressions and it will evaluate to
* <code>false</code>.
*/
ServiceRegistration() : ServiceRegistrationBase()
{
}
///@{
/**
* Returns a <code>ServiceReference</code> object for a service being
* registered.
* <p>
* The <code>ServiceReference</code> object may be shared with other
* modules.
*
* @throws std::logic_error If this
* <code>ServiceRegistration</code> object has already been
* unregistered or if it is invalid.
* @return <code>ServiceReference</code> object.
*/
ServiceReference<I1> GetReference(InterfaceType<I1>) const
{
return this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I1>());
}
ServiceReference<I2> GetReference(InterfaceType<I2>) const
{
return this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I2>());
}
ServiceReference<I3> GetReference(InterfaceType<I3>) const
{
return this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I3>());
}
///@}
using ServiceRegistrationBase::operator=;
private:
friend class ModuleContext;
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
};
/// \cond
template<class I1, class I2>
class ServiceRegistration<I1, I2, void> : public ServiceRegistrationBase
{
public:
ServiceRegistration() : ServiceRegistrationBase()
{
}
ServiceReference<I1> GetReference(InterfaceType<I1>) const
{
return ServiceReference<I1>(this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I1>()));
}
ServiceReference<I2> GetReference(InterfaceType<I2>) const
{
return ServiceReference<I2>(this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I2>()));
}
using ServiceRegistrationBase::operator=;
private:
friend class ModuleContext;
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
};
template<class I1>
class ServiceRegistration<I1, void, void> : public ServiceRegistrationBase
{
public:
ServiceRegistration() : ServiceRegistrationBase()
{
}
ServiceReference<I1> GetReference() const
{
return this->GetReference(InterfaceType<I1>());
}
ServiceReference<I1> GetReference(InterfaceType<I1>) const
{
return ServiceReference<I1>(this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I1>()));
}
using ServiceRegistrationBase::operator=;
private:
friend class ModuleContext;
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
};
template<>
class ServiceRegistration<void, void, void> : public ServiceRegistrationBase
{
public:
/**
* Creates an invalid ServiceReference object. You can use
* this object in boolean expressions and it will evaluate to
* <code>false</code>.
*/
ServiceRegistration() : ServiceRegistrationBase()
{
}
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
using ServiceRegistrationBase::operator=;
};
/// \endcond
/**
* \ingroup MicroServices
*
* A service registration object of unknown type.
*/
typedef ServiceRegistration<void> ServiceRegistrationU;
US_END_NAMESPACE
#endif // USSERVICEREGISTRATION_H
diff --git a/Modules/CppMicroServices/core/include/usServiceTracker.h b/Modules/CppMicroServices/core/include/usServiceTracker.h
index abff1471e6..daa7f03005 100644
--- a/Modules/CppMicroServices/core/include/usServiceTracker.h
+++ b/Modules/CppMicroServices/core/include/usServiceTracker.h
@@ -1,597 +1,597 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICETRACKER_H
#define USSERVICETRACKER_H
#include <map>
#include "usServiceReference.h"
#include "usServiceTrackerCustomizer.h"
#include "usLDAPFilter.h"
US_BEGIN_NAMESPACE
template<class S, class T> class TrackedService;
template<class S, class T> class ServiceTrackerPrivate;
class ModuleContext;
/**
* \ingroup MicroServices
*
* A base class template for type traits for objects tracked by a
* ServiceTracker instance. It provides the \c TrackedType typedef
* and two dummy method definitions.
*
* Tracked type traits (TTT) classes must additionally provide the
* following methods:
*
* <ul>
* <li><em>static bool IsValid(const TrackedType& t)</em> Returns \c true if \c t is a valid object, \c false otherwise.</li>
* <li><em>static void Dispose(TrackedType& t)</em> Clears any resources held by the tracked object \c t.</li>
* <li><em>static TrackedType DefaultValue()</em> Returns the default value for newly created tracked objects.</li>
* </ul>
*
* @tparam T The type of the tracked object.
* @tparam TTT The tracked type traits class deriving from this class.
*
* @see ServiceTracker
*/
template<class T, class TTT>
struct TrackedTypeTraitsBase
{
typedef T TrackedType;
// Needed for S == void
static TrackedType ConvertToTrackedType(const InterfaceMap&)
{
throw std::runtime_error("A custom ServiceTrackerCustomizer instance is required for custom tracked objects.");
//return TTT::DefaultValue();
}
// Needed for S != void
static TrackedType ConvertToTrackedType(void*)
{
throw std::runtime_error("A custom ServiceTrackerCustomizer instance is required for custom tracked objects.");
//return TTT::DefaultValue();
}
};
/// \cond
template<class S, class T>
struct TrackedTypeTraits;
/// \endcond
/**
* \ingroup MicroServices
*
* Default type traits for custom tracked objects of pointer type.
*
* Use this tracked type traits template for custom tracked objects of
* pointer type with the ServiceTracker class.
*
* @tparam S The type of the service being tracked.
* @tparam T The type of the tracked object.
*/
template<class S, class T>
struct TrackedTypeTraits<S,T*> : public TrackedTypeTraitsBase<T*,TrackedTypeTraits<S,T*> >
{
typedef T* TrackedType;
static bool IsValid(const TrackedType& t)
{
return t != nullptr;
}
static TrackedType DefaultValue()
{
return nullptr;
}
static void Dispose(TrackedType& t)
{
t = nullptr;
}
};
/// \cond
template<class S>
struct TrackedTypeTraits<S,S*>
{
typedef S* TrackedType;
static bool IsValid(const TrackedType& t)
{
return t != nullptr;
}
static TrackedType DefaultValue()
{
return nullptr;
}
static void Dispose(TrackedType& t)
{
t = nullptr;
}
static TrackedType ConvertToTrackedType(S* s)
{
return s;
}
};
/// \endcond
/// \cond
/*
* This specialization is "special" because the tracked type is not
* void* (as specified in the second template parameter) but InterfaceMap.
* This is in line with the ModuleContext::GetService(...) overloads to
- * return either S* or InterfaceMap dependening on the template parameter.
+ * return either S* or InterfaceMap depending on the template parameter.
*/
template<>
struct TrackedTypeTraits<void,void*>
{
typedef InterfaceMap TrackedType;
static bool IsValid(const TrackedType& t)
{
return !t.empty();
}
static TrackedType DefaultValue()
{
return TrackedType();
}
static void Dispose(TrackedType& t)
{
t.clear();
}
static TrackedType ConvertToTrackedType(const InterfaceMap& im)
{
return im;
}
};
/// \endcond
/**
* \ingroup MicroServices
*
* The <code>ServiceTracker</code> class simplifies using services from the
* framework's service registry.
* <p>
* A <code>ServiceTracker</code> object is constructed with search criteria and
* a <code>ServiceTrackerCustomizer</code> object. A <code>ServiceTracker</code>
* can use a <code>ServiceTrackerCustomizer</code> to customize the service
* objects to be tracked. The <code>ServiceTracker</code> can then be opened to
* begin tracking all services in the framework's service registry that match
* the specified search criteria. The <code>ServiceTracker</code> correctly
* handles all of the details of listening to <code>ServiceEvent</code>s and
* getting and ungetting services.
* <p>
* The <code>GetServiceReferences</code> method can be called to get references
* to the services being tracked. The <code>GetService</code> and
* <code>GetServices</code> methods can be called to get the service objects for
* the tracked service.
*
* \note The <code>ServiceTracker</code> class is thread-safe. It does not call a
* <code>ServiceTrackerCustomizer</code> while holding any locks.
* <code>ServiceTrackerCustomizer</code> implementations must also be
* thread-safe.
*
* Customization of the services to be tracked requires a custom tracked type traits
* class if the custom tracked type is not a pointer type. To customize a tracked
* service using a custom type with value-semantics like
* \snippet uServices-servicetracker/main.cpp tt
* the custom tracked type traits class should look like this:
* \snippet uServices-servicetracker/main.cpp ttt
*
* For a custom tracked type, a ServiceTrackerCustomizer is required, which knows
* how to associate the tracked service with the custom tracked type:
* \snippet uServices-servicetracker/main.cpp customizer
* The custom tracking type traits class and customizer can now be used to instantiate
* a ServiceTracker:
* \snippet uServices-servicetracker/main.cpp tracker
*
* If the custom tracked type is a pointer type, a suitable tracked type traits
* template is provided by the framework and only a ServiceTrackerCustomizer needs
* to be provided:
* \snippet uServices-servicetracker/main.cpp tracker2
*
*
* @tparam S The type of the service being tracked. The type S* must be an
* assignable datatype.
* @tparam TTT Type traits of the tracked object. The type traits class provides
* information about the customized service object, see TrackedTypeTraitsBase.
*
* @remarks This class is thread safe.
*/
template<class S, class TTT = TrackedTypeTraits<S,S*> >
class ServiceTracker : protected ServiceTrackerCustomizer<S,typename TTT::TrackedType>
{
public:
/// The type of the service being tracked
typedef S ServiceType;
/// The type of the tracked object
typedef typename TTT::TrackedType T;
typedef ServiceReference<S> ServiceReferenceType;
typedef std::map<ServiceReference<S>,T> TrackingMap;
~ServiceTracker() override;
/**
* Create a <code>ServiceTracker</code> on the specified
* <code>ServiceReference</code>.
*
* <p>
* The service referenced by the specified <code>ServiceReference</code>
* will be tracked by this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param reference The <code>ServiceReference</code> for the service to be
* tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is <code>null</code>, then this
* <code>ServiceTracker</code> will be used as the
* <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context,
const ServiceReferenceType& reference,
ServiceTrackerCustomizer<S,T>* customizer = nullptr);
/**
* Create a <code>ServiceTracker</code> on the specified class name.
*
* <p>
* Services registered under the specified class name will be tracked by
* this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param clazz The class name of the services to be tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is <code>null</code>, then this
* <code>ServiceTracker</code> will be used as the
* <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context, const std::string& clazz,
ServiceTrackerCustomizer<S,T>* customizer = 0);
/**
* Create a <code>ServiceTracker</code> on the specified
* <code>LDAPFilter</code> object.
*
* <p>
* Services which match the specified <code>LDAPFilter</code> object will be
* tracked by this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param filter The <code>LDAPFilter</code> to select the services to be
* tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is null, then this <code>ServiceTracker</code> will be
* used as the <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context, const LDAPFilter& filter,
ServiceTrackerCustomizer<S,T>* customizer = nullptr);
/**
* Create a <code>ServiceTracker</code> on the class template
* argument S.
*
* <p>
* Services registered under the interface name of the class template
* argument S will be tracked by this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is null, then this <code>ServiceTracker</code> will be
* used as the <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context, ServiceTrackerCustomizer<S,T>* customizer = nullptr);
/**
* Open this <code>ServiceTracker</code> and begin tracking services.
*
* <p>
* Services which match the search criteria specified when this
* <code>ServiceTracker</code> was created are now tracked by this
* <code>ServiceTracker</code>.
*
* @throws std::logic_error If the <code>ModuleContext</code>
* with which this <code>ServiceTracker</code> was created is no
* longer valid.
*/
virtual void Open();
/**
* Close this <code>ServiceTracker</code>.
*
* <p>
* This method should be called when this <code>ServiceTracker</code> should
* end the tracking of services.
*
* <p>
* This implementation calls GetServiceReferences() to get the list
* of tracked services to remove.
*/
virtual void Close();
/**
* Wait for at least one service to be tracked by this
* <code>ServiceTracker</code>. This method will also return when this
* <code>ServiceTracker</code> is closed.
*
* <p>
* It is strongly recommended that <code>WaitForService</code> is not used
* during the calling of the <code>ModuleActivator</code> methods.
* <code>ModuleActivator</code> methods are expected to complete in a short
* period of time.
*
* <p>
* This implementation calls GetService() to determine if a service
* is being tracked.
*
* @return Returns the result of GetService().
*/
virtual T WaitForService(unsigned long timeoutMillis = 0);
/**
* Return a list of <code>ServiceReference</code>s for all services being
* tracked by this <code>ServiceTracker</code>.
*
* @return List of <code>ServiceReference</code>s.
*/
virtual std::vector<ServiceReferenceType> GetServiceReferences() const;
/**
* Returns a <code>ServiceReference</code> for one of the services being
* tracked by this <code>ServiceTracker</code>.
*
* <p>
* If multiple services are being tracked, the service with the highest
* ranking (as specified in its <code>service.ranking</code> property) is
* returned. If there is a tie in ranking, the service with the lowest
* service ID (as specified in its <code>service.id</code> property); that
* is, the service that was registered first is returned. This is the same
* algorithm used by <code>ModuleContext::GetServiceReference()</code>.
*
* <p>
* This implementation calls GetServiceReferences() to get the list
* of references for the tracked services.
*
* @return A <code>ServiceReference</code> for a tracked service.
* @throws ServiceException if no services are being tracked.
*/
virtual ServiceReferenceType GetServiceReference() const;
/**
* Returns the service object for the specified
* <code>ServiceReference</code> if the specified referenced service is
* being tracked by this <code>ServiceTracker</code>.
*
* @param reference The reference to the desired service.
* @return A service object or <code>null</code> if the service referenced
* by the specified <code>ServiceReference</code> is not being
* tracked.
*/
virtual T GetService(const ServiceReferenceType& reference) const;
/**
* Return a list of service objects for all services being tracked by this
* <code>ServiceTracker</code>.
*
* <p>
* This implementation calls GetServiceReferences() to get the list
* of references for the tracked services and then calls
* GetService(const ServiceReference&) for each reference to get the
* tracked service object.
*
* @return A list of service objects or an empty list if no services
* are being tracked.
*/
virtual std::vector<T> GetServices() const;
/**
* Returns a service object for one of the services being tracked by this
* <code>ServiceTracker</code>.
*
* <p>
* If any services are being tracked, this implementation returns the result
* of calling <code>%GetService(%GetServiceReference())</code>.
*
* @return A service object or <code>null</code> if no services are being
* tracked.
*/
virtual T GetService() const;
/**
* Remove a service from this <code>ServiceTracker</code>.
*
* The specified service will be removed from this
* <code>ServiceTracker</code>. If the specified service was being tracked
* then the <code>ServiceTrackerCustomizer::RemovedService</code> method will
* be called for that service.
*
* @param reference The reference to the service to be removed.
*/
virtual void Remove(const ServiceReferenceType& reference);
/**
* Return the number of services being tracked by this
* <code>ServiceTracker</code>.
*
* @return The number of services being tracked.
*/
virtual int Size() const;
/**
* Returns the tracking count for this <code>ServiceTracker</code>.
*
* The tracking count is initialized to 0 when this
* <code>ServiceTracker</code> is opened. Every time a service is added,
* modified or removed from this <code>ServiceTracker</code>, the tracking
* count is incremented.
*
* <p>
* The tracking count can be used to determine if this
* <code>ServiceTracker</code> has added, modified or removed a service by
* comparing a tracking count value previously collected with the current
* tracking count value. If the value has not changed, then no service has
* been added, modified or removed from this <code>ServiceTracker</code>
* since the previous tracking count was collected.
*
* @return The tracking count for this <code>ServiceTracker</code> or -1 if
* this <code>ServiceTracker</code> is not open.
*/
virtual int GetTrackingCount() const;
/**
* Return a sorted map of the <code>ServiceReference</code>s and
* service objects for all services being tracked by this
* <code>ServiceTracker</code>. The map is sorted in natural order
* of <code>ServiceReference</code>. That is, the last entry is the service
* with the highest ranking and the lowest service id.
*
* @param tracked A <code>TrackingMap</code> with the <code>ServiceReference</code>s
* and service objects for all services being tracked by this
* <code>ServiceTracker</code>. If no services are being tracked,
* then the returned map is empty.
*/
virtual void GetTracked(TrackingMap& tracked) const;
/**
* Return if this <code>ServiceTracker</code> is empty.
*
* @return <code>true</code> if this <code>ServiceTracker</code> is not tracking any
* services.
*/
virtual bool IsEmpty() const;
protected:
/**
* Default implementation of the
* <code>ServiceTrackerCustomizer::AddingService</code> method.
*
* <p>
* This method is only called when this <code>ServiceTracker</code> has been
* constructed with a <code>null</code> ServiceTrackerCustomizer argument.
*
* <p>
* This implementation returns the result of calling <code>GetService</code>
* on the <code>ModuleContext</code> with which this
* <code>ServiceTracker</code> was created passing the specified
* <code>ServiceReference</code>.
* <p>
* This method can be overridden in a subclass to customize the service
* object to be tracked for the service being added. In that case, take care
* not to rely on the default implementation of
* \link RemovedService(const ServiceReferenceType&, T service) removedService\endlink
* to unget the service.
*
* @param reference The reference to the service being added to this
* <code>ServiceTracker</code>.
* @return The service object to be tracked for the service added to this
* <code>ServiceTracker</code>.
* @see ServiceTrackerCustomizer::AddingService(const ServiceReference&)
*/
T AddingService(const ServiceReferenceType& reference) override;
/**
* Default implementation of the
* <code>ServiceTrackerCustomizer::ModifiedService</code> method.
*
* <p>
* This method is only called when this <code>ServiceTracker</code> has been
* constructed with a <code>null</code> ServiceTrackerCustomizer argument.
*
* <p>
* This implementation does nothing.
*
* @param reference The reference to modified service.
* @param service The service object for the modified service.
* @see ServiceTrackerCustomizer::ModifiedService(const ServiceReference&, T)
*/
void ModifiedService(const ServiceReferenceType& reference, T service) override;
/**
* Default implementation of the
* <code>ServiceTrackerCustomizer::RemovedService</code> method.
*
* <p>
* This method is only called when this <code>ServiceTracker</code> has been
* constructed with a <code>null</code> ServiceTrackerCustomizer argument.
*
* <p>
* This implementation calls <code>UngetService</code>, on the
* <code>ModuleContext</code> with which this <code>ServiceTracker</code>
* was created, passing the specified <code>ServiceReference</code>.
* <p>
* This method can be overridden in a subclass. If the default
* implementation of \link AddingService(const ServiceReferenceType&) AddingService\endlink
* method was used, this method must unget the service.
*
* @param reference The reference to removed service.
* @param service The service object for the removed service.
* @see ServiceTrackerCustomizer::RemovedService(const ServiceReferenceType&, T)
*/
void RemovedService(const ServiceReferenceType& reference, T service) override;
private:
typedef ServiceTracker<S,TTT> _ServiceTracker;
typedef TrackedService<S,TTT> _TrackedService;
typedef ServiceTrackerPrivate<S,TTT> _ServiceTrackerPrivate;
typedef ServiceTrackerCustomizer<S,T> _ServiceTrackerCustomizer;
friend class TrackedService<S,TTT>;
friend class ServiceTrackerPrivate<S,TTT>;
_ServiceTrackerPrivate* const d;
};
US_END_NAMESPACE
#include "usServiceTracker.tpp"
#endif // USSERVICETRACKER_H
diff --git a/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h b/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h
index 85af6542d6..70b9c5995c 100644
--- a/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h
+++ b/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h
@@ -1,76 +1,76 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USCOREMODULECONTEXT_H
#define USCOREMODULECONTEXT_H
#include "usServiceListeners_p.h"
#include "usServiceRegistry_p.h"
#include "usModuleHooks_p.h"
#include "usServiceHooks_p.h"
US_BEGIN_NAMESPACE
/**
* This class is not part of the public API.
*/
class CoreModuleContext
{
public:
/**
* All listeners in this framework.
*/
ServiceListeners listeners;
/**
* All registered services in this framework.
*/
ServiceRegistry services;
/**
* All service hooks.
*/
ServiceHooks serviceHooks;
/**
* All module hooks.
*/
ModuleHooks moduleHooks;
/**
- * Contruct a core context
+ * Construct a core context
*
*/
CoreModuleContext();
~CoreModuleContext();
void Init();
void Uninit();
};
US_END_NAMESPACE
#endif // USCOREMODULECONTEXT_H
diff --git a/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h b/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h
index f3a0be1ca9..15fd96f4da 100644
--- a/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h
+++ b/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h
@@ -1,152 +1,152 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICEREFERENCEBASEPRIVATE_H
#define USSERVICEREFERENCEBASEPRIVATE_H
#include "usAtomicInt_p.h"
#include "usServiceInterface.h"
#include <string>
US_BEGIN_NAMESPACE
class Any;
class Module;
class ServicePropertiesImpl;
class ServiceRegistrationBasePrivate;
class ServiceReferenceBasePrivate;
/**
* \ingroup MicroServices
*/
class ServiceReferenceBasePrivate
{
public:
ServiceReferenceBasePrivate(ServiceRegistrationBasePrivate* reg);
~ServiceReferenceBasePrivate();
/**
* Get the service object.
*
* @param module requester of service.
* @return Service requested or null in case of failure.
*/
void* GetService(Module* module);
InterfaceMap GetServiceInterfaceMap(Module* module);
/**
* Get new service instance.
*
* @param module requester of service.
* @return Service requested or null in case of failure.
*/
InterfaceMap GetPrototypeService(Module* module);
/**
* Unget the service object.
*
* @param module Module who wants remove service.
- * @param checkRefCounter If true decrement refence counter and remove service
+ * @param checkRefCounter If true decrement reference counter and remove service
* if we reach zero. If false remove service without
- * checking refence counter.
+ * checking reference counter.
* @return True if service was removed or false if only reference counter was
* decremented.
*/
bool UngetService(Module* module, bool checkRefCounter);
/**
* Unget prototype scope service objects.
*
* @param module Module who wants to remove a prototype scope service.
* @param service The prototype scope service pointer.
* @return \c true if the service was removed, \c false otherwise.
*/
bool UngetPrototypeService(Module* module, void* service);
bool UngetPrototypeService(Module* module, const InterfaceMap& service);
/**
* Get all properties registered with this service.
*
* @return A ServiceProperties object containing properties or being empty
* if service has been removed.
*/
const ServicePropertiesImpl& GetProperties() const;
/**
* Returns the property value to which the specified property key is mapped
* in the properties <code>ServiceProperties</code> object of the service
* referenced by this <code>ServiceReference</code> object.
*
* <p>
* Property keys are case-insensitive.
*
* <p>
* This method must continue to return property values after the service has
* been unregistered. This is so references to unregistered services can
* still be interrogated.
*
* @param key The property key.
* @param lock If <code>true</code>, access of the properties of the service
* referenced by this <code>ServiceReference</code> object will be
* synchronized.
* @return The property value to which the key is mapped; an invalid Any
* if there is no property named after the key.
*/
Any GetProperty(const std::string& key, bool lock) const;
bool IsConvertibleTo(const std::string& interfaceId) const;
/**
* Reference count for implicitly shared private implementation.
*/
AtomicInt ref;
/**
* Link to registration object for this reference.
*/
ServiceRegistrationBasePrivate* const registration;
/**
* The service interface id for this reference.
*/
std::string interfaceId;
private:
InterfaceMap GetServiceFromFactory(Module* module, ServiceFactory* factory,
bool isModuleScope);
// purposely not implemented
ServiceReferenceBasePrivate(const ServiceReferenceBasePrivate&);
ServiceReferenceBasePrivate& operator=(const ServiceReferenceBasePrivate&);
};
US_END_NAMESPACE
#endif // USSERVICEREFERENCEBASEPRIVATE_H
diff --git a/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h b/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h
index 7f0d74e277..4e55d4a4ad 100644
--- a/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h
+++ b/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h
@@ -1,189 +1,189 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USSERVICEREGISTRY_H
#define USSERVICEREGISTRY_H
#include "usServiceInterface.h"
#include "usServiceRegistration.h"
#include "usThreads_p.h"
US_BEGIN_NAMESPACE
class CoreModuleContext;
class ModulePrivate;
class ServicePropertiesImpl;
/**
* Here we handle all the CppMicroServices services that are registered.
*/
class ServiceRegistry
{
public:
typedef Mutex MutexType;
mutable MutexType mutex;
/**
* Creates a new ServiceProperties object containing <code>in</code>
* with the keys converted to lower case.
*
* @param classes A list of class names which will be added to the
* created ServiceProperties object under the key
* ModuleConstants::OBJECTCLASS.
* @param sid A service id which will be used instead of a default one.
*/
static ServicePropertiesImpl CreateServiceProperties(const ServiceProperties& in,
const std::vector<std::string>& classes = std::vector<std::string>(),
bool isFactory = false, bool isPrototypeFactory = false, long sid = -1);
typedef US_UNORDERED_MAP_TYPE<ServiceRegistrationBase, std::vector<std::string> > MapServiceClasses;
typedef US_UNORDERED_MAP_TYPE<std::string, std::vector<ServiceRegistrationBase> > MapClassServices;
/**
* All registered services in the current framework.
* Mapping of registered service to class names under which
- * the service is registerd.
+ * the service is registered.
*/
MapServiceClasses services;
std::vector<ServiceRegistrationBase> serviceRegistrations;
/**
* Mapping of classname to registered service.
* The List of registered services are ordered with the highest
* ranked service first.
*/
MapClassServices classServices;
CoreModuleContext* core;
ServiceRegistry(CoreModuleContext* coreCtx);
~ServiceRegistry();
void Clear();
/**
* Register a service in the framework wide register.
*
* @param module The module registering the service.
* @param classes The class names under which the service can be located.
* @param service The service object.
* @param properties The properties for this service.
* @return A ServiceRegistration object.
* @exception std::invalid_argument If one of the following is true:
* <ul>
* <li>The service object is 0.</li>
* <li>The service parameter is not a ServiceFactory or an
* instance of all the named classes in the classes parameter.</li>
* </ul>
*/
ServiceRegistrationBase RegisterService(ModulePrivate* module,
const InterfaceMap& service,
const ServiceProperties& properties);
/**
* Service ranking changed, reorder registered services
* according to ranking.
*
* @param serviceRegistration The ServiceRegistrationPrivate object.
* @param rank New rank of object.
*/
void UpdateServiceRegistrationOrder(const ServiceRegistrationBase& sr,
const std::vector<std::string>& classes);
/**
* Get all services implementing a certain class.
* Only used internally by the framework.
*
* @param clazz The class name of the requested service.
* @return A sorted list of {@link ServiceRegistrationPrivate} objects.
*/
void Get(const std::string& clazz, std::vector<ServiceRegistrationBase>& serviceRegs) const;
/**
* Get a service implementing a certain class.
*
* @param module The module requesting reference
* @param clazz The class name of the requested service.
* @return A {@link ServiceReference} object.
*/
ServiceReferenceBase Get(ModulePrivate* module, const std::string& clazz) const;
/**
* Get all services implementing a certain class and then
* filter these with a property filter.
*
* @param clazz The class name of requested service.
* @param filter The property filter.
* @param module The module requesting reference.
* @return A list of {@link ServiceReference} object.
*/
void Get(const std::string& clazz, const std::string& filter,
ModulePrivate* module, std::vector<ServiceReferenceBase>& serviceRefs) const;
/**
* Remove a registered service.
*
* @param sr The ServiceRegistration object that is registered.
*/
void RemoveServiceRegistration(const ServiceRegistrationBase& sr) ;
/**
* Get all services that a module has registered.
*
* @param p The module
* @return A set of {@link ServiceRegistration} objects
*/
void GetRegisteredByModule(ModulePrivate* m, std::vector<ServiceRegistrationBase>& serviceRegs) const;
/**
* Get all services that a module uses.
*
* @param p The module
* @return A set of {@link ServiceRegistration} objects
*/
void GetUsedByModule(Module* m, std::vector<ServiceRegistrationBase>& serviceRegs) const;
private:
friend class ServiceHooks;
void Get_unlocked(const std::string& clazz, std::vector<ServiceRegistrationBase>& serviceRegs) const;
void Get_unlocked(const std::string& clazz, const std::string& filter,
ModulePrivate* module, std::vector<ServiceReferenceBase>& serviceRefs) const;
// purposely not implemented
ServiceRegistry(const ServiceRegistry&);
ServiceRegistry& operator=(const ServiceRegistry&);
};
US_END_NAMESPACE
#endif // USSERVICEREGISTRY_H
diff --git a/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h b/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h
index a020bd1391..50b429addc 100644
--- a/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h
+++ b/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h
@@ -1,376 +1,376 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USWAITCONDITION_P_H
#define USWAITCONDITION_P_H
#include "usCoreConfig.h"
#include "usLog_p.h"
#include "usUtils_p.h"
#include "usThreads_p.h"
#ifdef US_PLATFORM_POSIX
#include <sys/time.h>
#include <cerrno>
#endif
US_BEGIN_NAMESPACE
/**
* \brief A thread synchronization object used to suspend execution until some
* condition on shared data is met.
*
* A thread calls Wait() to suspend its execution until the condition is
* met. Each call to Notify() from an executing thread will then cause a single
* waiting thread to be released. A call to Notify() means, "signal
* that the condition is true." NotifyAll() releases all threads waiting on
* the condition variable.
*
* The WaitCondition implementation is consistent with the standard
* definition and use of condition variables in pthreads and other common
* thread libraries.
*
* IMPORTANT: A condition variable always requires an associated mutex
* object. The mutex object is used to avoid a dangerous race condition when
* Wait() and Notify() are called simultaneously from two different
* threads.
*
* On systems using pthreads, this implementation abstracts the
* standard calls to the pthread condition variable. On Win32
* systems, there is no system provided condition variable. This
* class implements a condition variable using a critical section, a
- * semphore, an event and a number of counters. The implementation is
+ * semaphore, an event and a number of counters. The implementation is
* almost an extract translation of the implementation presented by
* Douglas C Schmidt and Irfan Pyarali in "Strategies for Implementing
* POSIX Condition Variables on Win32".
*/
template<class MutexHost>
class WaitCondition
{
public:
WaitCondition();
~WaitCondition();
bool Wait(unsigned long time = 0);
/** Notify that the condition is true and release one waiting thread */
void Notify();
/** Notify that the condition is true and release all waiting threads */
void NotifyAll();
private:
// purposely not implemented
WaitCondition(const WaitCondition& other);
const WaitCondition& operator=(const WaitCondition&);
#ifdef US_ENABLE_THREADING_SUPPORT
/** Suspend execution of this thread until the condition is signaled. The
* argument is a SimpleMutex object that must be locked prior to calling
* this method. */
bool Wait(Mutex& mutex, unsigned long time = 0);
bool Wait(Mutex* mutex, unsigned long time = 0);
#ifdef US_PLATFORM_POSIX
pthread_cond_t m_WaitCondition;
#else
int m_NumberOfWaiters; // number of waiting threads
CRITICAL_SECTION m_NumberOfWaitersLock; // Serialize access to
// m_NumberOfWaiters
HANDLE m_Semaphore; // Semaphore to queue threads
HANDLE m_WaitersAreDone; // Auto-reset event used by the
// broadcast/signal thread to
// wait for all the waiting
// threads to wake up and
// release the semaphore
std::size_t m_WasNotifyAll; // Keeps track of whether we
// were broadcasting or signaling
#endif
#endif // US_ENABLE_THREADING_SUPPORT
};
template<class MutexHost>
class NoWaitCondition
{
public:
NoWaitCondition() {}
private:
// purposely not implemented
NoWaitCondition(const NoWaitCondition& other);
const NoWaitCondition& operator=(const NoWaitCondition&);
};
// ------------------------------------------------------------------------
// WaitCondition implementation
// ------------------------------------------------------------------------
#ifdef US_ENABLE_THREADING_SUPPORT
template<class MutexHost>
WaitCondition<MutexHost>::WaitCondition()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_init(&m_WaitCondition, nullptr);
#else
m_NumberOfWaiters = 0;
m_WasNotifyAll = 0;
m_Semaphore = CreateSemaphore(0, // no security
0, // initial value
0x7fffffff, // max count
0); // unnamed
InitializeCriticalSection(&m_NumberOfWaitersLock);
m_WaitersAreDone = CreateEvent(0, // no security
FALSE, // auto-reset
FALSE, // non-signaled initially
0 ); // unnamed
#endif
}
template<class MutexHost>
WaitCondition<MutexHost>::~WaitCondition()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_destroy(&m_WaitCondition);
#else
CloseHandle(m_Semaphore);
CloseHandle(m_WaitersAreDone);
DeleteCriticalSection(&m_NumberOfWaitersLock);
#endif
}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(unsigned long time)
{
return this->Wait(static_cast<MutexHost*>(this)->m_Mtx, time);
}
template<class MutexHost>
void WaitCondition<MutexHost>::Notify()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_signal(&m_WaitCondition);
#else
EnterCriticalSection(&m_NumberOfWaitersLock);
int haveWaiters = m_NumberOfWaiters > 0;
LeaveCriticalSection(&m_NumberOfWaitersLock);
// if there were not any waiters, then this is a no-op
if (haveWaiters)
{
ReleaseSemaphore(m_Semaphore, 1, 0);
}
#endif
}
template<class MutexHost>
void WaitCondition<MutexHost>::NotifyAll()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_broadcast(&m_WaitCondition);
#else
// This is needed to ensure that m_NumberOfWaiters and m_WasNotifyAll are
// consistent
EnterCriticalSection(&m_NumberOfWaitersLock);
int haveWaiters = 0;
if (m_NumberOfWaiters > 0)
{
// We are broadcasting, even if there is just one waiter...
// Record that we are broadcasting, which helps optimize Notify()
// for the non-broadcast case
m_WasNotifyAll = 1;
haveWaiters = 1;
}
if (haveWaiters)
{
// Wake up all waiters atomically
ReleaseSemaphore(m_Semaphore, m_NumberOfWaiters, 0);
LeaveCriticalSection(&m_NumberOfWaitersLock);
// Wait for all the awakened threads to acquire the counting
// semaphore
WaitForSingleObject(m_WaitersAreDone, INFINITE);
// This assignment is ok, even without the m_NumberOfWaitersLock held
// because no other waiter threads can wake up to access it.
m_WasNotifyAll = 0;
}
else
{
LeaveCriticalSection(&m_NumberOfWaitersLock);
}
#endif
}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(Mutex* mutex, unsigned long timeoutMillis)
{
return Wait(*mutex, timeoutMillis);
}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(Mutex& mutex, unsigned long timeoutMillis)
{
#ifdef US_PLATFORM_POSIX
struct timespec ts, * pts = nullptr;
if (timeoutMillis)
{
pts = &ts;
struct timeval tv;
int error = gettimeofday(&tv, nullptr);
if (error)
{
US_ERROR << "gettimeofday error: " << GetLastErrorStr();
return false;
}
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
ts.tv_sec += timeoutMillis / 1000;
ts.tv_nsec += (timeoutMillis % 1000) * 1000000;
ts.tv_sec += ts.tv_nsec / 1000000000;
ts.tv_nsec = ts.tv_nsec % 1000000000;
}
if (pts)
{
int error = pthread_cond_timedwait(&m_WaitCondition, &mutex.m_Mtx, pts);
if (error == 0)
{
return true;
}
else
{
if (error != ETIMEDOUT)
{
US_ERROR << "pthread_cond_timedwait error: " << GetLastErrorStr();
}
return false;
}
}
else
{
int error = pthread_cond_wait(&m_WaitCondition, &mutex.m_Mtx);
if (error)
{
US_ERROR << "pthread_cond_wait error: " << GetLastErrorStr();
return false;
}
return true;
}
#else
// Avoid race conditions
EnterCriticalSection(&m_NumberOfWaitersLock);
m_NumberOfWaiters++;
LeaveCriticalSection(&m_NumberOfWaitersLock);
DWORD dw;
bool result = true;
// This call atomically releases the mutex and waits on the
// semaphore until signaled
dw = SignalObjectAndWait(mutex.m_Mtx, m_Semaphore, timeoutMillis ? timeoutMillis : INFINITE, FALSE);
if (dw == WAIT_TIMEOUT)
{
result = false;
}
else if (dw == WAIT_FAILED)
{
result = false;
US_ERROR << "SignalObjectAndWait failed: " << GetLastErrorStr();
}
// Reacquire lock to avoid race conditions
EnterCriticalSection(&m_NumberOfWaitersLock);
// We're no longer waiting....
m_NumberOfWaiters--;
// Check to see if we're the last waiter after the broadcast
int lastWaiter = m_WasNotifyAll && m_NumberOfWaiters == 0;
LeaveCriticalSection(&m_NumberOfWaitersLock);
// If we're the last waiter thread during this particular broadcast
// then let the other threads proceed
if (lastWaiter)
{
// This call atomically signals the m_WaitersAreDone event and waits
// until it can acquire the external mutex. This is required to
// ensure fairness
dw = SignalObjectAndWait(m_WaitersAreDone, mutex.m_Mtx,
INFINITE, FALSE);
if (result && dw == WAIT_FAILED)
{
result = false;
US_ERROR << "SignalObjectAndWait failed: " << GetLastErrorStr();
}
}
else
{
- // Always regain the external mutex since that's the guarentee we
+ // Always regain the external mutex since that's the guarantee we
// give to our callers
dw = WaitForSingleObject(mutex.m_Mtx, INFINITE);
if (result && dw == WAIT_FAILED)
{
result = false;
US_ERROR << "SignalObjectAndWait failed: " << GetLastErrorStr();
}
}
return result;
#endif
}
#else
template<class MutexHost>
WaitCondition<MutexHost>::WaitCondition() {}
template<class MutexHost>
WaitCondition<MutexHost>::~WaitCondition() {}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(unsigned long)
{
return true;
}
template<class MutexHost>
void WaitCondition<MutexHost>::Notify() {}
template<class MutexHost>
void WaitCondition<MutexHost>::NotifyAll() {}
#endif
US_END_NAMESPACE
#endif // USWAITCONDITION_P_H
diff --git a/Modules/CppMicroServices/core/test/usModuleTest.cpp b/Modules/CppMicroServices/core/test/usModuleTest.cpp
index 713d16e17f..0c944b663a 100644
--- a/Modules/CppMicroServices/core/test/usModuleTest.cpp
+++ b/Modules/CppMicroServices/core/test/usModuleTest.cpp
@@ -1,350 +1,350 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#include <usModule.h>
#include <usModuleEvent.h>
#include <usServiceEvent.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usModuleRegistry.h>
#include <usModuleActivator.h>
#include <usModuleSettings.h>
#include <usSharedLibrary.h>
#include "usTestUtilModuleListener.h"
#include "usTestDriverActivator.h"
#include "usTestingMacros.h"
#include "usTestingConfig.h"
US_USE_NAMESPACE
namespace {
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
static const char PATH_SEPARATOR = '\\';
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
static const char PATH_SEPARATOR = '/';
#endif
// Check that the executable's activator was loaded and called
void frame01()
{
US_TEST_CONDITION_REQUIRED(TestDriverActivator::LoadCalled(), "ModuleActivator::Load() called for executable")
}
// Verify that the same member function pointers registered as listeners
// with different receivers works.
void frame02a()
{
ModuleContext* mc = GetModuleContext();
TestModuleListener listener1;
TestModuleListener listener2;
try
{
mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged);
mc->AddModuleListener(&listener1, &TestModuleListener::ModuleChanged);
mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged);
mc->AddModuleListener(&listener2, &TestModuleListener::ModuleChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_FAILED_MSG( << "module listener registration failed " << ise.what()
<< " : frameSL02a:FAIL" );
}
SharedLibrary target(LIB_PATH, "TestModuleA");
#ifdef US_BUILD_SHARED_LIBS
// Start the test target
try
{
target.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG( << "Failed to load module, got exception: "
<< e.what() << " + in frameSL02a:FAIL" );
}
#endif
Module* moduleA = ModuleRegistry::GetModule("TestModuleA");
US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA")
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA));
#endif
std::vector<ServiceEvent> seEvts;
US_TEST_CONDITION(listener1.CheckListenerEvents(pEvts, seEvts), "Check first module listener")
US_TEST_CONDITION(listener2.CheckListenerEvents(pEvts, seEvts), "Check second module listener")
mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged);
mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged);
target.Unload();
}
// Verify information from the ModuleInfo struct
void frame005a(ModuleContext* mc)
{
Module* m = mc->GetModule();
// check expected headers
US_TEST_CONDITION("main" == m->GetName(), "Test module name")
US_TEST_CONDITION(ModuleVersion(0,1,0) == m->GetVersion(), "Test test driver module version")
US_TEST_CONDITION(ModuleVersion(CppMicroServices_MAJOR_VERSION, CppMicroServices_MINOR_VERSION, CppMicroServices_PATCH_VERSION) == ModuleRegistry::GetModule(1)->GetVersion(), "Test CppMicroServices version")
}
// Get context id, location and status of the module
void frame010a(ModuleContext* mc)
{
Module* m = mc->GetModule();
long int contextid = m->GetModuleId();
US_DEBUG << "CONTEXT ID:" << contextid;
std::string location = m->GetLocation();
US_DEBUG << "LOCATION:" << location;
US_TEST_CONDITION(!location.empty(), "Test for non-empty module location")
US_TEST_CONDITION(m->IsLoaded(), "Test for loaded flag")
US_TEST_CONDITION(ModuleSettings::GetStoragePath().empty(), "Test for empty base storage path")
US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("").empty(), "Test for empty data path")
US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("bla").empty(), "Test for empty data file path")
}
//----------------------------------------------------------------------------
//Test result of GetService(ServiceReference()). Should throw std::invalid_argument
void frame018a(ModuleContext* mc)
{
try
{
mc->GetService(ServiceReferenceU());
US_DEBUG << "Got service object, expected std::invalid_argument exception";
- US_TEST_FAILED_MSG(<< "Got service object, excpected std::invalid_argument exception")
+ US_TEST_FAILED_MSG(<< "Got service object, expected std::invalid_argument exception")
}
catch (const std::invalid_argument& )
{}
catch (...)
{
US_TEST_FAILED_MSG(<< "Got wrong exception, expected std::invalid_argument")
}
}
// Load libA and check that it exists and that the service it registers exists,
// also check that the expected events occur and that the storage paths are correct
void frame020a(ModuleContext* mc, TestModuleListener& listener,
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary& libA)
{
try
{
libA.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
#else
SharedLibrary& /*libA*/)
{
#endif
ModuleSettings::SetStoragePath(std::string("/tmp") + PATH_SEPARATOR);
US_TEST_CONDITION(ModuleSettings::GetStoragePath() == "/tmp", "Test for valid base storage path")
Module* moduleA = ModuleRegistry::GetModule("TestModuleA");
US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA")
US_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name")
std::cout << moduleA->GetModuleContext()->GetDataFile("") << std::endl;
std::stringstream ss;
ss << moduleA->GetModuleId();
const std::string baseStoragePath = std::string("/tmp") + PATH_SEPARATOR + ss.str() + "_TestModuleA" + PATH_SEPARATOR;
US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("") == baseStoragePath, "Test for valid data path")
US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("bla") == baseStoragePath + "bla", "Test for valid data file path")
// Check if libA registered the expected service
try
{
ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleAService");
InterfaceMap o1 = mc->GetService(sr1);
US_TEST_CONDITION(!o1.empty(), "Test if service object found");
try
{
US_TEST_CONDITION(mc->UngetService(sr1), "Test if Service UnGet returns true");
}
catch (const std::logic_error& le)
{
US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what())
}
// check the listeners for events
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, sr1));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
catch (const ServiceException& /*se*/)
{
US_TEST_FAILED_MSG(<< "test module, expected service not found");
}
US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly");
}
// Unload libA and check for correct events
void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libA)
{
Module* moduleA = ModuleRegistry::GetModule("TestModuleA");
US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for non-null module")
ServiceReferenceU sr1
= mc->GetServiceReference("us::TestModuleAService");
US_TEST_CONDITION(sr1, "Test for valid service reference")
try
{
libA.Unload();
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(moduleA->IsLoaded() == false, "Test for unloaded state")
#endif
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what())
}
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleA));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleA));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, sr1));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
struct LocalListener {
void ServiceChanged(const ServiceEvent) {}
};
// Add a service listener with a broken LDAP filter to Get an exception
void frame045a(ModuleContext* mc)
{
LocalListener sListen1;
std::string brokenFilter = "A broken LDAP filter";
try
{
mc->AddServiceListener(&sListen1, &LocalListener::ServiceChanged, brokenFilter);
}
catch (const std::invalid_argument& /*ia*/)
{
//assertEquals("InvalidSyntaxException.GetFilter should be same as input string", brokenFilter, ise.GetFilter());
}
catch (...)
{
US_TEST_FAILED_MSG(<< "test module, wrong exception on broken LDAP filter:");
}
}
} // end unnamed namespace
int usModuleTest(int /*argc*/, char* /*argv*/[])
{
//US_TEST_BEGIN("ModuleTest");
std::vector<Module*> modules = ModuleRegistry::GetModules();
for (std::vector<Module*>::iterator iter = modules.begin(), iterEnd = modules.end();
iter != iterEnd; ++iter)
{
std::cout << "----- " << (*iter)->GetName() << std::endl;
}
frame01();
frame02a();
ModuleContext* mc = GetModuleContext();
TestModuleListener listener;
try
{
mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_OUTPUT( << "module listener registration failed " << ise.what() );
throw;
}
try
{
mc->AddServiceListener(&listener, &TestModuleListener::ServiceChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_OUTPUT( << "service listener registration failed " << ise.what() );
throw;
}
frame005a(mc);
frame010a(mc);
frame018a(mc);
SharedLibrary libA(LIB_PATH, "TestModuleA");
frame020a(mc, listener, libA);
frame030b(mc, listener, libA);
frame045a(mc);
mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged);
mc->RemoveServiceListener(&listener, &TestModuleListener::ServiceChanged);
//US_TEST_END()
return 0;
}
diff --git a/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp b/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp
index 46b4ed2a44..ccb20a1149 100644
--- a/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp
+++ b/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp
@@ -1,414 +1,414 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#include <usModule.h>
#include <usModuleEvent.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usLDAPProp.h>
#include <usServiceFindHook.h>
#include <usServiceEventListenerHook.h>
#include <usServiceListenerHook.h>
#include <usSharedLibrary.h>
#include "usTestingMacros.h"
#include "usTestingConfig.h"
US_USE_NAMESPACE
namespace {
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
class TestServiceListener
{
public:
void ServiceChanged(const ServiceEvent serviceEvent)
{
this->events.push_back(serviceEvent);
}
std::vector<ServiceEvent> events;
};
class TestServiceEventListenerHook : public ServiceEventListenerHook
{
private:
int id;
public:
TestServiceEventListenerHook(int id)
: id(id)
{
}
typedef ShrinkableMap<ModuleContext*, ShrinkableVector<ServiceListenerHook::ListenerInfo> > MapType;
void Event(const ServiceEvent& /*event*/, MapType& listeners) override
{
US_TEST_CONDITION_REQUIRED(listeners.size() > 0 && listeners.find(GetModuleContext()) != listeners.end(), "Check listener content");
ShrinkableVector<ServiceListenerHook::ListenerInfo>& listenerInfos = listeners[GetModuleContext()];
// listener count should be 2 because the event listener hooks are called with
- // the list of listeners before filtering them according to ther LDAP filter
+ // the list of listeners before filtering them according to their LDAP filter
if (id == 1)
{
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(listenerInfos.size() == 2, "2 service listeners expected");
#else
US_TEST_CONDITION(listenerInfos.size() >= 2, "2 service listeners expected");
#endif
US_TEST_CONDITION(listenerInfos[0].IsRemoved() == false, "Listener is not removed");
US_TEST_CONDITION(listenerInfos[1].IsRemoved() == false, "Listener is not removed");
US_TEST_CONDITION(!(listenerInfos[0] == listenerInfos[1]), "listener info inequality");
}
else
{
// there is already one listener filtered out
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(listenerInfos.size() == 1, "1 service listener expected");
#else
US_TEST_CONDITION(listenerInfos.size() >= 1, "1 service listener expected");
#endif
US_TEST_CONDITION(listenerInfos[0].IsRemoved() == false, "Listener is not removed");
}
if (listenerInfo.IsNull())
{
listenerInfo = listenerInfos[0];
}
else
{
US_TEST_CONDITION(listenerInfo == listenerInfos[0], "Equal listener info objects");
}
// Remove the listener without a filter from the list
for(ShrinkableVector<ServiceListenerHook::ListenerInfo>::iterator infoIter = listenerInfos.begin();
infoIter != listenerInfos.end();)
{
if (infoIter->GetFilter().empty())
{
infoIter = listenerInfos.erase(infoIter);
}
else
{
++infoIter;
}
}
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(listenerInfos.size() == 1, "One listener with LDAP filter should remain");
#else
US_TEST_CONDITION(listenerInfos.size() >= 1, "One listener with LDAP filter should remain");
#endif
ordering.push_back(id);
}
ServiceListenerHook::ListenerInfo listenerInfo;
static std::vector<int> ordering;
};
std::vector<int> TestServiceEventListenerHook::ordering;
class TestServiceFindHook : public ServiceFindHook
{
private:
int id;
public:
TestServiceFindHook(int id)
: id(id)
{
}
void Find(const ModuleContext* context, const std::string& /*name*/,
const std::string& /*filter*/, ShrinkableVector<ServiceReferenceBase>& references) override
{
US_TEST_CONDITION(context == GetModuleContext(), "Module context");
references.clear();
ordering.push_back(id);
}
static std::vector<int> ordering;
};
std::vector<int> TestServiceFindHook::ordering;
class TestServiceListenerHook : public ServiceListenerHook
{
private:
int id;
public:
TestServiceListenerHook(int id)
: id(id)
{
}
void Added(const std::vector<ListenerInfo>& listeners) override
{
for (std::vector<ListenerInfo>::const_iterator iter = listeners.begin();
iter != listeners.end(); ++iter)
{
if (iter->IsRemoved() || iter->GetModuleContext() != GetModuleContext()) continue;
listenerInfos.insert(*iter);
lastAdded = listeners.back();
ordering.push_back(id);
}
}
void Removed(const std::vector<ListenerInfo>& listeners) override
{
for (std::vector<ListenerInfo>::const_iterator iter = listeners.begin();
iter != listeners.end(); ++iter)
{
listenerInfos.erase(*iter);
ordering.push_back(id*10);
}
lastRemoved = listeners.back();
}
static std::vector<int> ordering;
US_UNORDERED_SET_TYPE<ListenerInfo> listenerInfos;
ListenerInfo lastAdded;
ListenerInfo lastRemoved;
};
std::vector<int> TestServiceListenerHook::ordering;
void TestEventListenerHook()
{
ModuleContext* context = GetModuleContext();
TestServiceListener serviceListener1;
TestServiceListener serviceListener2;
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->AddServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "bla");
TestServiceEventListenerHook serviceEventListenerHook1(1);
ServiceProperties hookProps1;
hookProps1[ServiceConstants::SERVICE_RANKING()] = 10;
ServiceRegistration<ServiceEventListenerHook> eventListenerHookReg1 =
context->RegisterService<ServiceEventListenerHook>(&serviceEventListenerHook1, hookProps1);
TestServiceEventListenerHook serviceEventListenerHook2(2);
ServiceProperties hookProps2;
hookProps2[ServiceConstants::SERVICE_RANKING()] = 0;
ServiceRegistration<ServiceEventListenerHook> eventListenerHookReg2 =
context->RegisterService<ServiceEventListenerHook>(&serviceEventListenerHook2, hookProps2);
std::vector<int> expectedOrdering;
expectedOrdering.push_back(1);
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
US_TEST_CONDITION(serviceEventListenerHook1.ordering == expectedOrdering, "Event listener hook call order");
US_TEST_CONDITION(serviceListener1.events.empty(), "service event of service event listener hook");
US_TEST_CONDITION(serviceListener2.events.empty(), "no service event for filtered listener");
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary libA(LIB_PATH, "TestModuleA");
try
{
libA.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
US_TEST_CONDITION(serviceEventListenerHook1.ordering == expectedOrdering, "Event listener hook call order");
libA.Unload();
#endif
US_TEST_CONDITION(serviceListener1.events.empty(), "no service event due to service event listener hook");
US_TEST_CONDITION(serviceListener2.events.empty(), "no service event for filtered listener due to service event listener hook");
eventListenerHookReg2.Unregister();
eventListenerHookReg1.Unregister();
context->RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged);
}
void TestListenerHook()
{
ModuleContext* context = GetModuleContext();
TestServiceListener serviceListener1;
TestServiceListener serviceListener2;
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->AddServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "bla");
TestServiceListenerHook serviceListenerHook1(1);
ServiceProperties hookProps1;
hookProps1[ServiceConstants::SERVICE_RANKING()] = 0;
ServiceRegistration<ServiceListenerHook> listenerHookReg1 =
context->RegisterService<ServiceListenerHook>(&serviceListenerHook1, hookProps1);
TestServiceListenerHook serviceListenerHook2(2);
ServiceProperties hookProps2;
hookProps2[ServiceConstants::SERVICE_RANKING()] = 10;
ServiceRegistration<ServiceListenerHook> listenerHookReg2 =
context->RegisterService<ServiceListenerHook>(&serviceListenerHook2, hookProps2);
#ifdef US_BUILD_SHARED_LIBS
// check if hooks got notified about the existing listeners
US_TEST_CONDITION_REQUIRED(serviceListenerHook1.listenerInfos.size() == 2, "Notification about existing listeners")
#endif
const std::size_t listenerInfoSizeOld = serviceListenerHook1.listenerInfos.size() - 2;
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
ServiceListenerHook::ListenerInfo lastAdded = serviceListenerHook1.lastAdded;
#ifdef US_BUILD_SHARED_LIBS
std::vector<int> expectedOrdering;
expectedOrdering.push_back(1);
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
expectedOrdering.push_back(2);
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order");
#endif
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "blub");
US_TEST_CONDITION(lastAdded == serviceListenerHook1.lastRemoved, "Same ListenerInfo object)");
US_TEST_CONDITION(!(lastAdded == serviceListenerHook1.lastAdded), "New ListenerInfo object)");
#ifdef US_BUILD_SHARED_LIBS
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order");
#endif
context->RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged);
#ifdef US_BUILD_SHARED_LIBS
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order");
#endif
US_TEST_CONDITION_REQUIRED(serviceListenerHook1.listenerInfos.size() == listenerInfoSizeOld, "Removed listener infos")
listenerHookReg2.Unregister();
listenerHookReg1.Unregister();
}
void TestFindHook()
{
ModuleContext* context = GetModuleContext();
TestServiceFindHook serviceFindHook1(1);
ServiceProperties hookProps1;
hookProps1[ServiceConstants::SERVICE_RANKING()] = 0;
ServiceRegistration<ServiceFindHook> findHookReg1 =
context->RegisterService<ServiceFindHook>(&serviceFindHook1, hookProps1);
TestServiceFindHook serviceFindHook2(2);
ServiceProperties hookProps2;
hookProps2[ServiceConstants::SERVICE_RANKING()] = 10;
ServiceRegistration<ServiceFindHook> findHookReg2 =
context->RegisterService<ServiceFindHook>(&serviceFindHook2, hookProps2);
std::vector<int> expectedOrdering;
US_TEST_CONDITION(serviceFindHook1.ordering == expectedOrdering, "Find hook call order");
TestServiceListener serviceListener;
context->AddServiceListener(&serviceListener, &TestServiceListener::ServiceChanged);
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary libA(LIB_PATH, "TestModuleA");
try
{
libA.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
US_TEST_CONDITION(serviceListener.events.size() == 1, "Service registered");
#endif
std::vector<ServiceReferenceU> refs = context->GetServiceReferences("us::TestModuleAService");
US_TEST_CONDITION(refs.empty(), "Empty references");
ServiceReferenceU ref = context->GetServiceReference("us::TestModuleAService");
US_TEST_CONDITION(!ref, "Invalid reference (filtered out)");
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
US_TEST_CONDITION(serviceFindHook1.ordering == expectedOrdering, "Find hook call order");
findHookReg2.Unregister();
findHookReg1.Unregister();
refs = context->GetServiceReferences("us::TestModuleAService");
US_TEST_CONDITION(!refs.empty(), "Non-empty references");
ref = context->GetServiceReference("us::TestModuleAService");
US_TEST_CONDITION(ref, "Valid reference");
#ifdef US_BUILD_SHARED_LIBS
libA.Unload();
#endif
context->RemoveServiceListener(&serviceListener, &TestServiceListener::ServiceChanged);
}
} // end unnamed namespace
int usServiceHooksTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("ServiceHooksTest");
TestListenerHook();
TestFindHook();
TestEventListenerHook();
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp b/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp
index cd80f8e2bb..2b1dc91d4c 100644
--- a/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp
+++ b/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp
@@ -1,565 +1,565 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#include <usTestingMacros.h>
#include <usTestingConfig.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usSharedLibrary.h>
#include <usModulePropsInterface.h>
US_USE_NAMESPACE
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
class TestServiceListener
{
private:
friend bool runLoadUnloadTest(const std::string&, int cnt, SharedLibrary&,
const std::vector<ServiceEvent::Type>&);
const bool checkUsingModules;
std::vector<ServiceEvent> events;
bool teststatus;
ModuleContext* mc;
public:
TestServiceListener(ModuleContext* mc, bool checkUsingModules = true)
: checkUsingModules(checkUsingModules), events(), teststatus(true), mc(mc)
{}
bool getTestStatus() const
{
return teststatus;
}
void clearEvents()
{
events.clear();
}
bool checkEvents(const std::vector<ServiceEvent::Type>& eventTypes)
{
if (events.size() != eventTypes.size())
{
dumpEvents(eventTypes);
return false;
}
for (std::size_t i=0; i < eventTypes.size(); ++i)
{
if (eventTypes[i] != events[i].GetType())
{
dumpEvents(eventTypes);
return false;
}
}
return true;
}
void serviceChanged(const ServiceEvent evt)
{
events.push_back(evt);
US_TEST_OUTPUT( << "ServiceEvent: " << evt );
if (ServiceEvent::UNREGISTERING == evt.GetType())
{
ServiceReferenceU sr = evt.GetServiceReference();
// Validate that no module is marked as using the service
std::vector<Module*> usingModules;
sr.GetUsingModules(usingModules);
if (checkUsingModules && !usingModules.empty())
{
teststatus = false;
printUsingModules(sr, "*** Using modules (unreg) should be empty but is: ");
}
// Check if the service can be fetched
InterfaceMap service = mc->GetService(sr);
sr.GetUsingModules(usingModules);
// if (UNREGISTERSERVICE_VALID_DURING_UNREGISTERING) {
// In this mode the service shall be obtainable during
// unregistration.
if (service.empty())
{
teststatus = false;
US_TEST_OUTPUT( << "*** Service should be available to ServiceListener "
<< "while handling unregistering event." );
}
US_TEST_OUTPUT( << "Service (unreg): " << service.begin()->first << " -> " << service.begin()->second );
if (checkUsingModules && usingModules.size() != 1)
{
teststatus = false;
printUsingModules(sr, "*** One using module expected "
"(unreg, after getService), found: ");
}
else
{
printUsingModules(sr, "Using modules (unreg, after getService): ");
}
// } else {
// // In this mode the service shall NOT be obtainable during
// // unregistration.
// if (null!=service) {
// teststatus = false;
// out.print("*** Service should not be available to ServiceListener "
// +"while handling unregistering event.");
// }
// if (checkUsingBundles && null!=usingBundles) {
// teststatus = false;
// printUsingBundles(sr,
// "*** Using bundles (unreg, after getService), "
// +"should be null but is: ");
// } else {
// printUsingBundles(sr,
// "Using bundles (unreg, after getService): null");
// }
// }
mc->UngetService(sr);
// Check that the UNREGISTERING service can not be looked up
// using the service registry.
try
{
long sid = any_cast<long>(sr.GetProperty(ServiceConstants::SERVICE_ID()));
std::stringstream ss;
ss << "(" << ServiceConstants::SERVICE_ID() << "=" << sid << ")";
std::vector<ServiceReferenceU> srs = mc->GetServiceReferences("", ss.str());
if (srs.empty())
{
US_TEST_OUTPUT( << "ServiceReference for UNREGISTERING service is not"
" found in the service registry; ok." );
}
else
{
teststatus = false;
US_TEST_OUTPUT( << "*** ServiceReference for UNREGISTERING service,"
<< sr << ", not found in the service registry; fail." );
US_TEST_OUTPUT( << "Found the following Service references:") ;
for(std::vector<ServiceReferenceU>::const_iterator sr = srs.begin();
sr != srs.end(); ++sr)
{
US_TEST_OUTPUT( << (*sr) );
}
}
}
catch (const std::exception& e)
{
teststatus = false;
- US_TEST_OUTPUT( << "*** Unexpected excpetion when trying to lookup a"
+ US_TEST_OUTPUT( << "*** Unexpected exception when trying to lookup a"
" service while it is in state UNREGISTERING: "
<< e.what() );
}
}
}
void printUsingModules(const ServiceReferenceU& sr, const std::string& caption)
{
std::vector<Module*> usingModules;
sr.GetUsingModules(usingModules);
US_TEST_OUTPUT( << (caption.empty() ? "Using modules: " : caption) );
for(std::vector<Module*>::const_iterator module = usingModules.begin();
module != usingModules.end(); ++module)
{
US_TEST_OUTPUT( << " -" << (*module) );
}
}
// Print expected and actual service events.
void dumpEvents(const std::vector<ServiceEvent::Type>& eventTypes)
{
std::size_t max = events.size() > eventTypes.size() ? events.size() : eventTypes.size();
US_TEST_OUTPUT( << "Expected event type -- Actual event" );
for (std::size_t i=0; i < max; ++i)
{
ServiceEvent evt = i < events.size() ? events[i] : ServiceEvent();
if (i < eventTypes.size())
{
US_TEST_OUTPUT( << " " << eventTypes[i] << "--" << evt );
}
else
{
US_TEST_OUTPUT( << " " << "- NONE - " << "--" << evt );
}
}
}
}; // end of class ServiceListener
bool runLoadUnloadTest(const std::string& name, int cnt, SharedLibrary& target,
const std::vector<ServiceEvent::Type>& events)
{
bool teststatus = true;
ModuleContext* mc = GetModuleContext();
for (int i = 0; i < cnt && teststatus; ++i)
{
TestServiceListener sListen(mc);
try
{
mc->AddServiceListener(&sListen, &TestServiceListener::serviceChanged);
//mc->AddServiceListener(MessageDelegate1<ServiceListener, const ServiceEvent&>(&sListen, &ServiceListener::serviceChanged));
}
catch (const std::logic_error& ise)
{
teststatus = false;
US_TEST_FAILED_MSG( << "service listener registration failed " << ise.what()
<< " :" << name << ":FAIL" );
}
// Start the test target to get a service published.
try
{
target.Load();
}
catch (const std::exception& e)
{
teststatus = false;
US_TEST_FAILED_MSG( << "Failed to load module, got exception: "
<< e.what() << " + in " << name << ":FAIL" );
}
// Stop the test target to get a service unpublished.
try
{
target.Unload();
}
catch (const std::exception& e)
{
teststatus = false;
US_TEST_FAILED_MSG( << "Failed to unload module, got exception: "
<< e.what() << " + in " << name << ":FAIL" );
}
if (teststatus && !sListen.checkEvents(events))
{
teststatus = false;
US_TEST_FAILED_MSG( << "Service listener event notification error :"
<< name << ":FAIL" );
}
try
{
mc->RemoveServiceListener(&sListen, &TestServiceListener::serviceChanged);
teststatus &= sListen.teststatus;
sListen.clearEvents();
}
catch (const std::logic_error& ise)
{
teststatus = false;
US_TEST_FAILED_MSG( << "service listener removal failed " << ise.what()
<< " :" << name << ":FAIL" );
}
}
return teststatus;
}
void frameSL02a()
{
ModuleContext* mc = GetModuleContext();
TestServiceListener listener1(mc);
TestServiceListener listener2(mc);
try
{
mc->RemoveServiceListener(&listener1, &TestServiceListener::serviceChanged);
mc->AddServiceListener(&listener1, &TestServiceListener::serviceChanged);
mc->RemoveServiceListener(&listener2, &TestServiceListener::serviceChanged);
mc->AddServiceListener(&listener2, &TestServiceListener::serviceChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_FAILED_MSG( << "service listener registration failed " << ise.what()
<< " : frameSL02a:FAIL" );
}
SharedLibrary target(LIB_PATH, "TestModuleA");
// Start the test target to get a service published.
try
{
target.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG( << "Failed to load module, got exception: "
<< e.what() << " + in frameSL02a:FAIL" );
}
std::vector<ServiceEvent::Type> events;
events.push_back(ServiceEvent::REGISTERED);
US_TEST_CONDITION(listener1.checkEvents(events), "Check first service listener")
US_TEST_CONDITION(listener2.checkEvents(events), "Check second service listener")
mc->RemoveServiceListener(&listener1, &TestServiceListener::serviceChanged);
mc->RemoveServiceListener(&listener2, &TestServiceListener::serviceChanged);
target.Unload();
}
void frameSL05a()
{
std::vector<ServiceEvent::Type> events;
events.push_back(ServiceEvent::REGISTERED);
events.push_back(ServiceEvent::UNREGISTERING);
SharedLibrary libA(LIB_PATH, "TestModuleA");
bool testStatus = runLoadUnloadTest("FrameSL05a", 1, libA, events);
US_TEST_CONDITION(testStatus, "FrameSL05a")
}
void frameSL10a()
{
std::vector<ServiceEvent::Type> events;
events.push_back(ServiceEvent::REGISTERED);
events.push_back(ServiceEvent::UNREGISTERING);
SharedLibrary libA2(LIB_PATH, "TestModuleA2");
bool testStatus = runLoadUnloadTest("FrameSL10a", 1, libA2, events);
US_TEST_CONDITION(testStatus, "FrameSL10a")
}
void frameSL25a()
{
ModuleContext* mc = GetModuleContext();
TestServiceListener sListen(mc, false);
try
{
mc->AddServiceListener(&sListen, &TestServiceListener::serviceChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_OUTPUT( << "service listener registration failed " << ise.what() );
throw;
}
SharedLibrary libSL1(LIB_PATH, "TestModuleSL1");
SharedLibrary libSL3(LIB_PATH, "TestModuleSL3");
SharedLibrary libSL4(LIB_PATH, "TestModuleSL4");
std::vector<ServiceEvent::Type> expectedServiceEventTypes;
// Startup
expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // at start of libSL1
expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // FooService at start of libSL4
expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // at start of libSL3
// Stop libSL4
expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // FooService at first stop of libSL4
#ifdef US_BUILD_SHARED_LIBS
// Shutdown
expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // at stop of libSL1
expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // at stop of libSL3
#endif
// Start libModuleTestSL1 to ensure that the Service interface is available.
try
{
US_TEST_OUTPUT( << "Starting libModuleTestSL1: " << libSL1.GetFilePath() );
libSL1.Load();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() );
throw;
}
- // Start libModuleTestSL4 that will require the serivce interface and publish
+ // Start libModuleTestSL4 that will require the service interface and publish
// us::FooService
try
{
US_TEST_OUTPUT( << "Starting libModuleTestSL4: " << libSL4.GetFilePath() );
libSL4.Load();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() );
throw;
}
- // Start libModuleTestSL3 that will require the serivce interface and get the service
+ // Start libModuleTestSL3 that will require the service interface and get the service
try
{
US_TEST_OUTPUT( << "Starting libModuleTestSL3: " << libSL3.GetFilePath() );
libSL3.Load();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() );
throw;
}
// Check that libSL3 has been notified about the FooService.
US_TEST_OUTPUT( << "Check that FooService is added to service tracker in libSL3" );
try
{
ServiceReferenceU libSL3SR = mc->GetServiceReference("ActivatorSL3");
InterfaceMap libSL3Activator = mc->GetService(libSL3SR);
US_TEST_CONDITION_REQUIRED(!libSL3Activator.empty(), "ActivatorSL3 service != 0");
ServiceReference<ModulePropsInterface> libSL3PropsI(libSL3SR);
ModulePropsInterface* propsInterface = mc->GetService(libSL3PropsI);
US_TEST_CONDITION_REQUIRED(propsInterface, "ModulePropsInterface != 0");
ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceAdded");
US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceAdded");
Any serviceAddedField3 = i->second;
US_TEST_CONDITION_REQUIRED(!serviceAddedField3.Empty() && any_cast<bool>(serviceAddedField3),
"libSL3 notified about presence of FooService");
mc->UngetService(libSL3SR);
}
catch (const ServiceException& se)
{
US_TEST_FAILED_MSG( << "Failed to get service reference:" << se );
}
// Check that libSL1 has been notified about the FooService.
US_TEST_OUTPUT( << "Check that FooService is added to service tracker in libSL1" );
try
{
ServiceReferenceU libSL1SR = mc->GetServiceReference("ActivatorSL1");
InterfaceMap libSL1Activator = mc->GetService(libSL1SR);
US_TEST_CONDITION_REQUIRED(!libSL1Activator.empty(), "ActivatorSL1 service != 0");
ServiceReference<ModulePropsInterface> libSL1PropsI(libSL1SR);
ModulePropsInterface* propsInterface = mc->GetService(libSL1PropsI);
US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface");
ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceAdded");
US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceAdded");
Any serviceAddedField1 = i->second;
US_TEST_CONDITION_REQUIRED(!serviceAddedField1.Empty() && any_cast<bool>(serviceAddedField1),
"libSL1 notified about presence of FooService");
mc->UngetService(libSL1SR);
}
catch (const ServiceException& se)
{
US_TEST_FAILED_MSG( << "Failed to get service reference:" << se );
}
// Stop the service provider: libSL4
try
{
US_TEST_OUTPUT( << "Stop libSL4: " << libSL4.GetFilePath() );
libSL4.Unload();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to unload module, got exception:" << e.what() );
throw;
}
// Check that libSL3 has been notified about the removal of FooService.
US_TEST_OUTPUT( << "Check that FooService is removed from service tracker in libSL3" );
try
{
ServiceReferenceU libSL3SR = mc->GetServiceReference("ActivatorSL3");
InterfaceMap libSL3Activator = mc->GetService(libSL3SR);
US_TEST_CONDITION_REQUIRED(!libSL3Activator.empty(), "ActivatorSL3 service != 0");
ServiceReference<ModulePropsInterface> libSL3PropsI(libSL3SR);
ModulePropsInterface* propsInterface = mc->GetService(libSL3PropsI);
US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface");
ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceRemoved");
US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceRemoved");
Any serviceRemovedField3 = i->second;
US_TEST_CONDITION(!serviceRemovedField3.Empty() && any_cast<bool>(serviceRemovedField3),
"libSL3 notified about removal of FooService");
mc->UngetService(libSL3SR);
}
catch (const ServiceException& se)
{
US_TEST_FAILED_MSG( << "Failed to get service reference:" << se );
}
// Stop libSL1
try
{
US_TEST_OUTPUT( << "Stop libSL1:" << libSL1.GetFilePath() );
libSL1.Unload();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to unload module got exception" << e.what() );
throw;
}
// Stop pSL3
try
{
US_TEST_OUTPUT( << "Stop libSL3:" << libSL3.GetFilePath() );
libSL3.Unload();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to unload module got exception" << e.what() );
throw;
}
// Check service events seen by this class
US_TEST_OUTPUT( << "Checking ServiceEvents(ServiceListener):" );
if (!sListen.checkEvents(expectedServiceEventTypes))
{
US_TEST_FAILED_MSG( << "Service listener event notification error" );
}
US_TEST_CONDITION_REQUIRED(sListen.getTestStatus(), "Service listener checks");
try
{
mc->RemoveServiceListener(&sListen, &TestServiceListener::serviceChanged);
sListen.clearEvents();
}
catch (const std::logic_error& ise)
{
US_TEST_FAILED_MSG( << "service listener removal failed: " << ise.what() );
}
}
int usServiceListenerTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("ServiceListenerTest");
frameSL02a();
frameSL05a();
frameSL10a();
frameSL25a();
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp b/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp
index a6cb2f70ef..68f8ae6cc6 100644
--- a/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp
+++ b/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp
@@ -1,287 +1,287 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#include <usTestingMacros.h>
#include <usTestingConfig.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usServiceInterface.h>
#include <usServiceTracker.h>
#include <usSharedLibrary.h>
#include "usServiceControlInterface.h"
#include <memory>
US_USE_NAMESPACE
bool CheckConvertibility(const std::vector<ServiceReferenceU>& refs,
std::vector<std::string>::const_iterator idBegin,
std::vector<std::string>::const_iterator idEnd)
{
std::vector<std::string> ids;
ids.assign(idBegin, idEnd);
for (std::vector<ServiceReferenceU>::const_iterator sri = refs.begin();
sri != refs.end(); ++sri)
{
for (std::vector<std::string>::iterator idIter = ids.begin();
idIter != ids.end(); ++idIter)
{
if (sri->IsConvertibleTo(*idIter))
{
ids.erase(idIter);
break;
}
}
}
return ids.empty();
}
struct MyInterfaceOne {
virtual ~MyInterfaceOne() {}
};
struct MyInterfaceTwo {
virtual ~MyInterfaceTwo() {}
};
class MyCustomizer : public us::ServiceTrackerCustomizer<MyInterfaceOne>
{
public:
MyCustomizer(us::ModuleContext* context)
: m_context(context)
{}
MyInterfaceOne* AddingService(const ServiceReferenceType& reference) override
{
US_TEST_CONDITION_REQUIRED(reference, "AddingService() valid reference")
return m_context->GetService(reference);
}
void ModifiedService(const ServiceReferenceType& reference, MyInterfaceOne* service) override
{
US_TEST_CONDITION(reference, "ModifiedService() valid reference")
US_TEST_CONDITION(service, "ModifiedService() valid service")
}
void RemovedService(const ServiceReferenceType& reference, MyInterfaceOne* service) override
{
US_TEST_CONDITION(reference, "RemovedService() valid reference")
US_TEST_CONDITION(service, "RemovedService() valid service")
}
private:
us::ModuleContext* m_context;
};
void TestFilterString()
{
us::ModuleContext* context = us::GetModuleContext();
MyCustomizer customizer(context);
us::LDAPFilter filter("(" + us::ServiceConstants::SERVICE_ID() + ">=0)");
us::ServiceTracker<MyInterfaceOne> tracker(context, filter, &customizer);
tracker.Open();
struct MyServiceOne : public MyInterfaceOne {};
struct MyServiceTwo : public MyInterfaceTwo {};
MyServiceOne serviceOne;
MyServiceTwo serviceTwo;
ServiceRegistration<MyInterfaceOne> reg1 = context->RegisterService<MyInterfaceOne>(&serviceOne);
ServiceRegistration<MyInterfaceTwo> reg2 = context->RegisterService<MyInterfaceTwo>(&serviceTwo);
US_TEST_CONDITION(tracker.GetServiceReferences().size() == 1, "tracking count")
reg1.Unregister();
reg2.Unregister();
}
void TestServiceTracker()
{
#ifdef US_PLATFORM_WINDOWS
const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
ModuleContext* mc = GetModuleContext();
SharedLibrary libS(LIB_PATH, "TestModuleS");
#ifdef US_BUILD_SHARED_LIBS
// Start the test target to get a service published.
try
{
libS.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() );
}
#endif
// 1. Create a ServiceTracker with ServiceTrackerCustomizer == null
std::string s1("us::TestModuleSService");
ServiceReferenceU servref = mc->GetServiceReference(s1 + "0");
US_TEST_CONDITION_REQUIRED(servref != nullptr, "Test if registered service of id us::TestModuleSService0");
ServiceReference<ServiceControlInterface> servCtrlRef = mc->GetServiceReference<ServiceControlInterface>();
- US_TEST_CONDITION_REQUIRED(servCtrlRef != nullptr, "Test if constrol service was registered");
+ US_TEST_CONDITION_REQUIRED(servCtrlRef != nullptr, "Test if control service was registered");
ServiceControlInterface* serviceController = mc->GetService(servCtrlRef);
US_TEST_CONDITION_REQUIRED(serviceController != nullptr, "Test valid service controller");
std::unique_ptr<ServiceTracker<void>> st1(new ServiceTracker<void>(mc, servref));
// 2. Check the size method with an unopened service tracker
US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Test if size == 0");
// 3. Open the service tracker and see what it finds,
// expect to find one instance of the implementation,
// "org.cppmicroservices.TestModuleSService0"
st1->Open();
std::vector<ServiceReferenceU> sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 1, "Checking ServiceTracker size");
US_TEST_CONDITION_REQUIRED(s1 + "0" == sa2[0].GetInterfaceId(), "Checking service implementation name");
// 5. Close this service tracker
st1->Close();
// 6. Check the size method, now when the servicetracker is closed
US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Checking ServiceTracker size");
// 7. Check if we still track anything , we should get null
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.empty(), "Checking ServiceTracker size");
// 8. A new Servicetracker, this time with a filter for the object
std::string fs = std::string("(") + ServiceConstants::OBJECTCLASS() + "=" + s1 + "*" + ")";
LDAPFilter f1(fs);
st1.reset(new ServiceTracker<void>(mc, f1));
// add a service
serviceController->ServiceControl(1, "register", 7);
// 9. Open the service tracker and see what it finds,
// expect to find two instances of references to
// "org.cppmicroservices.TestModuleSService*"
// i.e. they refer to the same piece of code
std::vector<std::string> ids;
ids.push_back((s1 + "0"));
ids.push_back((s1 + "1"));
ids.push_back((s1 + "2"));
ids.push_back((s1 + "3"));
st1->Open();
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Checking service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+2), "Check for expected interface id [0]");
US_TEST_CONDITION_REQUIRED(sa2[1].IsConvertibleTo(s1 + "1"), "Check for expected interface id [1]");
// 10. Get libTestModuleS to register one more service and see if it appears
serviceController->ServiceControl(2, "register", 1);
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+3), "Check for expected interface id [2]");
// 11. Get libTestModuleS to register one more service and see if it appears
serviceController->ServiceControl(3, "register", 2);
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 4, "Checking service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.end()), "Check for expected interface id [3]");
// 12. Get libTestModuleS to unregister one service and see if it disappears
serviceController->ServiceControl(3, "unregister", 0);
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count");
// 13. Get the highest ranking service reference, it should have ranking 7
ServiceReferenceU h1 = st1->GetServiceReference();
int rank = any_cast<int>(h1.GetProperty(ServiceConstants::SERVICE_RANKING()));
US_TEST_CONDITION_REQUIRED(rank == 7, "Check service rank");
// 14. Get the service of the highest ranked service reference
InterfaceMap o1 = st1->GetService(h1);
US_TEST_CONDITION_REQUIRED(!o1.empty(), "Check for non-null service");
// 14a Get the highest ranked service, directly this time
InterfaceMap o3 = st1->GetService();
US_TEST_CONDITION_REQUIRED(!o3.empty(), "Check for non-null service");
US_TEST_CONDITION_REQUIRED(o1 == o3, "Check for equal service instances");
// 15. Now release the tracking of that service and then try to get it
// from the servicetracker, which should yield a null object
serviceController->ServiceControl(1, "unregister", 7);
InterfaceMap o2 = st1->GetService(h1);
- US_TEST_CONDITION_REQUIRED(o2.empty(), "Checkt that service is null");
+ US_TEST_CONDITION_REQUIRED(o2.empty(), "Check that service is null");
// 16. Get all service objects this tracker tracks, it should be 2
std::vector<InterfaceMap> ts1 = st1->GetServices();
US_TEST_CONDITION_REQUIRED(ts1.size() == 2, "Check service count");
// 17. Test the remove method.
// First register another service, then remove it being tracked
serviceController->ServiceControl(1, "register", 7);
h1 = st1->GetServiceReference();
std::vector<ServiceReferenceU> sa3 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa3.size() == 3, "Check service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa3, ids.begin(), ids.begin()+3), "Check for expected interface id [0]");
st1->Remove(h1); // remove tracking on one servref
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Check service reference count");
// 18. Test the addingService method,add a service reference
// 19. Test the removedService method, remove a service reference
// 20. Test the waitForService method
InterfaceMap o9 = st1->WaitForService(50);
US_TEST_CONDITION_REQUIRED(!o9.empty(), "Checking WaitForService method");
}
int usServiceTrackerTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("ServiceTrackerTest")
TestFilterString();
TestServiceTracker();
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp b/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp
index ee433b74ed..d7fdc83f16 100644
--- a/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp
+++ b/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp
@@ -1,183 +1,183 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#include <usModule.h>
#include <usModuleEvent.h>
#include <usServiceEvent.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usModuleRegistry.h>
#include <usModuleActivator.h>
#include <usSharedLibrary.h>
#include "usTestUtilModuleListener.h"
#include "usTestingMacros.h"
#include "usTestingConfig.h"
US_USE_NAMESPACE
namespace {
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
// Load libTestModuleB and check that it exists and that the service it registers exists,
// also check that the expected events occur
void frame020a(ModuleContext* mc, TestModuleListener& listener,
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary& libB)
{
try
{
libB.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
#else
SharedLibrary& /*libB*/)
{
#endif
Module* moduleB = ModuleRegistry::GetModule("TestModuleB");
US_TEST_CONDITION_REQUIRED(moduleB != nullptr, "Test for existing module TestModuleB")
Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB");
US_TEST_CONDITION_REQUIRED(moduleImportedByB != nullptr, "Test for existing module TestModuleImportedByB")
US_TEST_CONDITION(moduleB->GetName() == "TestModuleB", "Test module name")
// Check if libB registered the expected service
try
{
std::vector<ServiceReferenceU> refs = mc->GetServiceReferences("us::TestModuleBService");
- US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Test that both the service from the shared and imported library are regsitered");
+ US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Test that both the service from the shared and imported library are registered");
InterfaceMap o1 = mc->GetService(refs.front());
US_TEST_CONDITION(!o1.empty(), "Test if first service object found");
InterfaceMap o2 = mc->GetService(refs.back());
US_TEST_CONDITION(!o2.empty(), "Test if second service object found");
try
{
US_TEST_CONDITION(mc->UngetService(refs.front()), "Test if Service UnGet for first service returns true");
US_TEST_CONDITION(mc->UngetService(refs.back()), "Test if Service UnGet for second service returns true");
}
catch (const std::logic_error &le)
{
US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what())
}
// check the listeners for events
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleB));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleB));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleImportedByB));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleImportedByB));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.back()));
seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.front()));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
catch (const ServiceException& /*se*/)
{
US_TEST_FAILED_MSG(<< "test module, expected service not found");
}
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(moduleB->IsLoaded() == true, "Test if loaded correctly");
#endif
}
// Unload libB and check for correct events
void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libB)
{
Module* moduleB = ModuleRegistry::GetModule("TestModuleB");
US_TEST_CONDITION_REQUIRED(moduleB != nullptr, "Test for non-null module")
Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB");
US_TEST_CONDITION_REQUIRED(moduleImportedByB != nullptr, "Test for non-null module")
std::vector<ServiceReferenceU> refs
= mc->GetServiceReferences("us::TestModuleBService");
US_TEST_CONDITION(refs.front(), "Test for first valid service reference")
US_TEST_CONDITION(refs.back(), "Test for second valid service reference")
try
{
libB.Unload();
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(moduleB->IsLoaded() == false, "Test for unloaded state")
#endif
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what())
}
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleImportedByB));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleImportedByB));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleB));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleB));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.front()));
seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.back()));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
} // end unnamed namespace
int usStaticModuleTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("StaticModuleTest");
ModuleContext* mc = GetModuleContext();
TestModuleListener listener;
ModuleListenerRegistrationHelper<TestModuleListener> ml(mc, &listener, &TestModuleListener::ModuleChanged);
ServiceListenerRegistrationHelper<TestModuleListener> sl(mc, &listener, &TestModuleListener::ServiceChanged);
SharedLibrary libB(LIB_PATH, "TestModuleB");
frame020a(mc, listener, libB);
frame030b(mc, listener, libB);
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usTestingMacros.h b/Modules/CppMicroServices/core/test/usTestingMacros.h
index 036cc1cbd2..ae3e0dfa5a 100644
--- a/Modules/CppMicroServices/core/test/usTestingMacros.h
+++ b/Modules/CppMicroServices/core/test/usTestingMacros.h
@@ -1,138 +1,138 @@
/*============================================================================
Library: CppMicroServices
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================*/
#ifndef USTESTINGMACROS_H_
#define USTESTINGMACROS_H_
#include <exception>
#include <string>
#include <iostream>
#include <cstdlib>
#include "usTestManager.h"
US_BEGIN_NAMESPACE
/** \brief Indicate a failed test. */
class TestFailedException : public std::exception {
public:
TestFailedException() {}
};
US_END_NAMESPACE
/**
*
* \brief Output some text without generating a terminating newline.
*
* */
#define US_TEST_OUTPUT_NO_ENDL(x) \
std::cout x << std::flush;
/** \brief Output some text. */
#define US_TEST_OUTPUT(x) \
US_TEST_OUTPUT_NO_ENDL(x << "\n")
/** \brief Do some general test preparations. Must be called first in the
main test function. */
#define US_TEST_BEGIN(testName) \
std::string usTestName(#testName); \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().Initialize(); \
try {
/** \brief Fail and finish test with message MSG */
#define US_TEST_FAILED_MSG(MSG) \
US_TEST_OUTPUT(MSG) \
throw US_PREPEND_NAMESPACE(TestFailedException)();
/** \brief Must be called last in the main test function. */
#define US_TEST_END() \
} catch (const US_PREPEND_NAMESPACE(TestFailedException)&) { \
US_TEST_OUTPUT(<< "Further test execution skipped.") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
} catch (const std::exception& ex) { \
- US_TEST_OUTPUT(<< "Exception occured " << ex.what()) \
+ US_TEST_OUTPUT(<< "Exception occurred " << ex.what()) \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
} \
if (US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfFailedTests() > 0) { \
US_TEST_OUTPUT(<< usTestName << ": [DONE FAILED] , subtests passed: " << \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfPassedTests() << " failed: " << \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfFailedTests() ) \
return EXIT_FAILURE; \
} else { \
US_TEST_OUTPUT(<< usTestName << ": " \
<< US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfPassedTests() \
<< " tests [DONE PASSED]") \
return EXIT_SUCCESS; \
}
#define US_TEST_CONDITION(COND,MSG) \
US_TEST_OUTPUT_NO_ENDL(<< MSG) \
if ( ! (COND) ) { \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
US_TEST_OUTPUT(<< " [FAILED]\n" << "In " << __FILE__ \
<< ", line " << __LINE__ \
<< ": " #COND " : [FAILED]") \
} else { \
US_TEST_OUTPUT(<< " [PASSED]") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \
}
#define US_TEST_CONDITION_REQUIRED(COND,MSG) \
US_TEST_OUTPUT_NO_ENDL(<< MSG) \
if ( ! (COND) ) { \
US_TEST_FAILED_MSG(<< " [FAILED]\n" << " +--> in " << __FILE__ \
<< ", line " << __LINE__ \
<< ", expression is false: \"" #COND "\"") \
} else { \
US_TEST_OUTPUT(<< " [PASSED]") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \
}
/**
* \brief Begin block which should be checked for exceptions
*
* This macro, together with US_TEST_FOR_EXCEPTION_END, can be used
* to test whether a code block throws an expected exception. The test FAILS if the
* exception is NOT thrown.
*/
#define US_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \
try {
#define US_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
US_TEST_OUTPUT( << "Expected an '" << #EXCEPTIONCLASS << "' exception. [FAILED]") \
} \
catch (const EXCEPTIONCLASS &) { \
US_TEST_OUTPUT(<< "Caught an expected '" << #EXCEPTIONCLASS \
<< "' exception. [PASSED]") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \
}
/**
* \brief Simplified version of US_TEST_FOR_EXCEPTION_BEGIN / END for
* a single statement
*/
#define US_TEST_FOR_EXCEPTION(EXCEPTIONCLASS, STATEMENT) \
US_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \
STATEMENT ; \
US_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS)
#endif // USTESTINGMACROS_H_
diff --git a/Modules/CppMicroServices/third_party/miniz.c b/Modules/CppMicroServices/third_party/miniz.c
index 044942157c..4b93de6fc5 100644
--- a/Modules/CppMicroServices/third_party/miniz.c
+++ b/Modules/CppMicroServices/third_party/miniz.c
@@ -1,4928 +1,4928 @@
/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
Implements RFC 1950: https://www.ietf.org/rfc/rfc1950.txt and RFC 1951: https://www.ietf.org/rfc/rfc1951.txt
Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
* Change History
10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!):
- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug
- would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
+ would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
(which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
- Merged MZ_FORCEINLINE fix from hdeanclark
- Fix <time.h> include before config #ifdef, thanks emil.brink
- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
set it to 1 for real-time compression).
- Merged in some compiler fixes from paulharris's github repro.
- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3.
- Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
- Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
5/28/11 v1.11 - Added statement from unlicense.org
5/27/11 v1.10 - Substantial compressor optimizations:
- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
- Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
- Refactored the compression code for better readability and maintainability.
- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
drop in throughput on some files).
5/15/11 v1.09 - Initial stable release.
* Low-level Deflate/Inflate implementation notes:
Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
approximately as well as zlib.
Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
block large enough to hold the entire file.
The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
* zlib-style API notes:
miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
zlib replacement in many apps:
The z_stream struct, optional memory allocation callbacks
deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
inflateInit/inflateInit2/inflate/inflateEnd
compress, compress2, compressBound, uncompress
CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
Supports raw deflate streams or standard zlib streams with adler-32 checking.
Limitations:
The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
there are no guarantees that miniz.c pulls this off perfectly.
* PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
Alex Evans. Supports 1-4 bytes/pixel images.
* ZIP archive API notes:
The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
existing archives, create new archives, append new files to existing archives, or clone archive data from
one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
or you can specify custom file read/write callbacks.
- Archive reading: Just call this function to read a single file from a disk archive:
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
size_t *pSize, mz_uint zip_flags);
For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
- Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
The locate operation can optionally check file comments too, which (as one example) can be used to identify
multiple versions of the same file in an archive. This function uses a simple linear search through the central
directory, so it's not very fast.
Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
retrieve detailed info on each file by calling mz_zip_reader_file_stat().
- Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
to disk and builds an exact image of the central directory in memory. The central directory image is written
all at once at the end of the archive file when the archive is finalized.
The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
which can be useful when the archive will be read from optical media. Also, the writer supports placing
arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
readable by any ZIP tool.
- Archive appending: The simple way to add a single file to an archive is to call this function:
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
The archive will be created if it doesn't already exist, otherwise it'll be appended to.
Note the appending is done in-place and is not an atomic operation, so if something goes wrong
during the operation it's possible the archive could be left without a central directory (although the local
file headers and file data will be fine, so the archive will be recoverable).
For more complex archive modification scenarios:
1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
you're done. This is safe but requires a bunch of temporary disk space or heap memory.
2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
append new files as needed, then finalize the archive which will write an updated central directory to the
original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
- ZIP archive support limitations:
No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
Requires streams capable of seeking.
* This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
* Important: For best perf. be sure to customize the below macros for your target platform:
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#define MINIZ_LITTLE_ENDIAN 1
#define MINIZ_HAS_64BIT_REGISTERS 1
* On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
(i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
*/
#ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED
#include <stdlib.h>
// Defines to completely disable specific portions of miniz.c:
// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.
// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
//#define MINIZ_NO_STDIO
// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
// get/set file times, and the C run-time funcs that get/set times won't be called.
// The current downside is the times written to your archives will be from 1979.
//#define MINIZ_NO_TIME
// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_APIS
// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_WRITING_APIS
// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
//#define MINIZ_NO_ZLIB_APIS
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
//#define MINIZ_NO_MALLOC
#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
// TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux
#define MINIZ_NO_TIME
#endif
#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
#include <time.h>
#endif
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
#define MINIZ_X86_OR_X64_CPU 1
#endif
#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
#define MINIZ_LITTLE_ENDIAN 1
#endif
#if MINIZ_X86_OR_X64_CPU
// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#endif
#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
// ------------------- zlib-style API Definitions.
// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
typedef unsigned long mz_ulong;
// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
void mz_free(void *p);
#define MZ_ADLER32_INIT (1)
// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
#define MZ_CRC32_INIT (0)
// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
// Compression strategies.
enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };
// Method
#define MZ_DEFLATED 8
#ifndef MINIZ_NO_ZLIB_APIS
// Heap allocation callbacks.
// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
typedef void (*mz_free_func)(void *opaque, void *address);
typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
#define MZ_VERSION "9.1.15"
#define MZ_VERNUM 0x91F0
#define MZ_VER_MAJOR 9
#define MZ_VER_MINOR 1
#define MZ_VER_REVISION 15
#define MZ_VER_SUBREVISION 0
// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
// Return status codes. MZ_PARAM_ERROR is non-standard.
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };
// Window bits
#define MZ_DEFAULT_WINDOW_BITS 15
struct mz_internal_state;
// Compression/decompression stream struct.
typedef struct mz_stream_s
{
const unsigned char *next_in; // pointer to next byte to read
unsigned int avail_in; // number of bytes available at next_in
mz_ulong total_in; // total number of bytes consumed so far
unsigned char *next_out; // pointer to next byte to write
unsigned int avail_out; // number of bytes that can be written to next_out
mz_ulong total_out; // total number of bytes produced so far
char *msg; // error msg (unused)
struct mz_internal_state *state; // internal state, allocated by zalloc/zfree
mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc)
mz_free_func zfree; // optional heap free function (defaults to free)
void *opaque; // heap alloc function user pointer
int data_type; // data_type (unused)
mz_ulong adler; // adler32 of the source or uncompressed data
mz_ulong reserved; // not used
} mz_stream;
typedef mz_stream *mz_streamp;
// Returns the version string of miniz.c.
const char *mz_version(void);
// mz_deflateInit() initializes a compressor with default options:
// Parameters:
// pStream must point to an initialized mz_stream struct.
// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
// Return values:
// MZ_OK on success.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_PARAM_ERROR if the input parameters are bogus.
// MZ_MEM_ERROR on out of memory.
int mz_deflateInit(mz_streamp pStream, int level);
// mz_deflateInit2() is like mz_deflate(), except with more control:
// Additional parameters:
// method must be MZ_DEFLATED
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
// mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
int mz_deflateReset(mz_streamp pStream);
// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
// Parameters:
// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
// Return values:
// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_PARAM_ERROR if one of the parameters is invalid.
// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
int mz_deflate(mz_streamp pStream, int flush);
// mz_deflateEnd() deinitializes a compressor:
// Return values:
// MZ_OK on success.
// MZ_STREAM_ERROR if the stream is bogus.
int mz_deflateEnd(mz_streamp pStream);
// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
// Single-call compression functions mz_compress() and mz_compress2():
// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
mz_ulong mz_compressBound(mz_ulong source_len);
// Initializes a decompressor.
int mz_inflateInit(mz_streamp pStream);
// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
int mz_inflateInit2(mz_streamp pStream, int window_bits);
// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
// Parameters:
// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
// Return values:
// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_DATA_ERROR if the deflate stream is invalid.
// MZ_PARAM_ERROR if one of the parameters is invalid.
// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
// with more input data, or with more room in the output buffer (except when using single call decompression, described above).
int mz_inflate(mz_streamp pStream, int flush);
// Deinitializes a decompressor.
int mz_inflateEnd(mz_streamp pStream);
// Single-call decompression.
// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
// Returns a string description of the specified error code, or NULL if the error code is invalid.
const char *mz_error(int err);
// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
typedef unsigned char Byte;
typedef unsigned int uInt;
typedef mz_ulong uLong;
typedef Byte Bytef;
typedef uInt uIntf;
typedef char charf;
typedef int intf;
typedef void *voidpf;
typedef uLong uLongf;
typedef void *voidp;
typedef void *const voidpc;
#define Z_NULL 0
#define Z_NO_FLUSH MZ_NO_FLUSH
#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH
#define Z_SYNC_FLUSH MZ_SYNC_FLUSH
#define Z_FULL_FLUSH MZ_FULL_FLUSH
#define Z_FINISH MZ_FINISH
#define Z_BLOCK MZ_BLOCK
#define Z_OK MZ_OK
#define Z_STREAM_END MZ_STREAM_END
#define Z_NEED_DICT MZ_NEED_DICT
#define Z_ERRNO MZ_ERRNO
#define Z_STREAM_ERROR MZ_STREAM_ERROR
#define Z_DATA_ERROR MZ_DATA_ERROR
#define Z_MEM_ERROR MZ_MEM_ERROR
#define Z_BUF_ERROR MZ_BUF_ERROR
#define Z_VERSION_ERROR MZ_VERSION_ERROR
#define Z_PARAM_ERROR MZ_PARAM_ERROR
#define Z_NO_COMPRESSION MZ_NO_COMPRESSION
#define Z_BEST_SPEED MZ_BEST_SPEED
#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION
#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY
#define Z_FILTERED MZ_FILTERED
#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY
#define Z_RLE MZ_RLE
#define Z_FIXED MZ_FIXED
#define Z_DEFLATED MZ_DEFLATED
#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
#define alloc_func mz_alloc_func
#define free_func mz_free_func
#define internal_state mz_internal_state
#define z_stream mz_stream
#define deflateInit mz_deflateInit
#define deflateInit2 mz_deflateInit2
#define deflateReset mz_deflateReset
#define deflate mz_deflate
#define deflateEnd mz_deflateEnd
#define deflateBound mz_deflateBound
#define compress mz_compress
#define compress2 mz_compress2
#define compressBound mz_compressBound
#define inflateInit mz_inflateInit
#define inflateInit2 mz_inflateInit2
#define inflate mz_inflate
#define inflateEnd mz_inflateEnd
#define uncompress mz_uncompress
#define crc32 mz_crc32
#define adler32 mz_adler32
#define MAX_WBITS 15
#define MAX_MEM_LEVEL 9
#define zError mz_error
#define ZLIB_VERSION MZ_VERSION
#define ZLIB_VERNUM MZ_VERNUM
#define ZLIB_VER_MAJOR MZ_VER_MAJOR
#define ZLIB_VER_MINOR MZ_VER_MINOR
#define ZLIB_VER_REVISION MZ_VER_REVISION
#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION
#define zlibVersion mz_version
#define zlib_version mz_version()
#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#endif // MINIZ_NO_ZLIB_APIS
// ------------------- Types and macros
typedef unsigned char mz_uint8;
typedef signed short mz_int16;
typedef unsigned short mz_uint16;
typedef unsigned int mz_uint32;
typedef unsigned int mz_uint;
typedef long long mz_int64;
typedef unsigned long long mz_uint64;
typedef int mz_bool;
#define MZ_FALSE (0)
#define MZ_TRUE (1)
// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message.
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
// ------------------- ZIP archive reading/writing
#ifndef MINIZ_NO_ARCHIVE_APIS
enum
{
MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
};
typedef struct
{
mz_uint32 m_file_index;
mz_uint32 m_central_dir_ofs;
mz_uint16 m_version_made_by;
mz_uint16 m_version_needed;
mz_uint16 m_bit_flag;
mz_uint16 m_method;
#ifndef MINIZ_NO_TIME
time_t m_time;
#endif
mz_uint32 m_crc32;
mz_uint64 m_comp_size;
mz_uint64 m_uncomp_size;
mz_uint16 m_internal_attr;
mz_uint32 m_external_attr;
mz_uint64 m_local_header_ofs;
mz_uint32 m_comment_size;
char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
} mz_zip_archive_file_stat;
typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
struct mz_zip_internal_state_tag;
typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
typedef enum
{
MZ_ZIP_MODE_INVALID = 0,
MZ_ZIP_MODE_READING = 1,
MZ_ZIP_MODE_WRITING = 2,
MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
} mz_zip_mode;
typedef struct mz_zip_archive_tag
{
mz_uint64 m_archive_size;
mz_uint64 m_archive_file_ofs;
mz_uint64 m_central_directory_file_ofs;
mz_uint m_total_files;
mz_zip_mode m_zip_mode;
mz_uint m_file_offset_alignment;
mz_alloc_func m_pAlloc;
mz_free_func m_pFree;
mz_realloc_func m_pRealloc;
void *m_pAlloc_opaque;
mz_file_read_func m_pRead;
mz_file_write_func m_pWrite;
void *m_pIO_opaque;
mz_zip_internal_state *m_pState;
} mz_zip_archive;
typedef enum
{
MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
} mz_zip_flags;
// ZIP archive reading
// Inits a ZIP archive reader.
// These functions read and validate the archive's central directory.
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
#endif
// Returns the total number of files in the archive.
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
// Returns detailed information about an archive file entry.
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
// Determines if an archive file entry is a directory entry.
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
// Retrieves the filename of an archive file entry.
// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
// Attempts to locates a file in the archive's central directory.
// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
// Returns -1 if the file cannot be found.
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
// Extracts a archive file to a memory buffer using no memory allocation.
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
// Extracts a archive file to a memory buffer.
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
// Extracts a archive file to a dynamically allocated heap buffer.
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
// Extracts a archive file using a callback function to output the file's data.
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
#ifndef MINIZ_NO_STDIO
// Extracts a archive file to a disk file and sets its last accessed and modified times.
// This function only extracts files, not archive directory records.
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
#endif
// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
// ZIP archive writing
#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
// Inits a ZIP archive writer.
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
#endif
// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
// the archive is finalized the file's central directory will be hosed.
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
#ifndef MINIZ_NO_STDIO
// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
#endif
// Adds a file to an archive by fully cloning the data from another archive.
// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);
// Finalizes the archive by writing the central directory records followed by the end of central directory record.
// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
// An archive must be manually finalized by calling this function for it to be valid.
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);
// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
// Note for the archive to be valid, it must have been finalized before ending.
mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
// Misc. high-level helper functions:
// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
// Reads a single file from an archive into a heap block.
// Returns NULL on failure.
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags);
#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
// ------------------- Low-level Decompression API Definitions
// Decompression flags used by tinfl_decompress().
// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
// High level decompression functions:
// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
// On return:
// Function returns a pointer to the decompressed data, or NULL on failure.
// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must call mz_free() on the returned block when it's no longer needed.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
// Returns 1 on success or 0 on failure.
typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
// Max size of LZ dictionary.
#define TINFL_LZ_DICT_SIZE 32768
// Return status.
typedef enum
{
TINFL_STATUS_BAD_PARAM = -3,
TINFL_STATUS_ADLER32_MISMATCH = -2,
TINFL_STATUS_FAILED = -1,
TINFL_STATUS_DONE = 0,
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
// Initializes the decompressor to its initial state.
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
// Internal/private bits follow.
enum
{
TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
typedef struct
{
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
// ------------------- Low-level Compression API Definitions
// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
#define TDEFL_LESS_MEMORY 0
// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
enum
{
TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
};
// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK).
enum
{
TDEFL_WRITE_ZLIB_HEADER = 0x01000,
TDEFL_COMPUTE_ADLER32 = 0x02000,
TDEFL_GREEDY_PARSING_FLAG = 0x04000,
TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
TDEFL_RLE_MATCHES = 0x10000,
TDEFL_FILTER_MATCHES = 0x20000,
TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,
TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000
};
// High level compression functions:
// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of source block to compress.
// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
// On return:
// Function returns a pointer to the compressed data, or NULL on failure.
// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must free() the returned block when it's no longer needed.
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
// Returns 0 on failure.
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// Compresses an image to a compressed PNG file in memory.
// On entry:
// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL
// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps).
// On return:
// Function returns a pointer to the compressed data, or NULL on failure.
// *pLen_out will be set to the size of the PNG image file.
// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };
// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
#if TDEFL_LESS_MEMORY
enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#else
enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#endif
// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
typedef enum
{
TDEFL_STATUS_BAD_PARAM = -2,
TDEFL_STATUS_PUT_BUF_FAILED = -1,
TDEFL_STATUS_OKAY = 0,
TDEFL_STATUS_DONE = 1,
} tdefl_status;
// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
typedef enum
{
TDEFL_NO_FLUSH = 0,
TDEFL_SYNC_FLUSH = 2,
TDEFL_FULL_FLUSH = 3,
TDEFL_FINISH = 4
} tdefl_flush;
// tdefl's compression state structure.
typedef struct
{
tdefl_put_buf_func_ptr m_pPut_buf_func;
void *m_pPut_buf_user;
mz_uint m_flags, m_max_probes[2];
int m_greedy_parsing;
mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
tdefl_status m_prev_return_status;
const void *m_pIn_buf;
void *m_pOut_buf;
size_t *m_pIn_buf_size, *m_pOut_buf_size;
tdefl_flush m_flush;
const mz_uint8 *m_pSrc;
size_t m_src_buf_left, m_out_buf_ofs;
mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
} tdefl_compressor;
// Initializes the compressor.
// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
// tdefl_compress_buffer() always consumes the entire input buffer.
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
#ifndef MINIZ_NO_ZLIB_APIS
// Create tdefl_compress() flags given zlib-style compression parameters.
// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
// window_bits may be -15 (raw deflate) or 15 (zlib)
// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
#endif // #ifndef MINIZ_NO_ZLIB_APIS
#ifdef __cplusplus
}
#endif
#endif // MINIZ_HEADER_INCLUDED
// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)
#ifndef MINIZ_HEADER_FILE_ONLY
typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1];
typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1];
typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1];
#include <string.h>
#include <assert.h>
#define MZ_ASSERT(x) assert(x)
#ifdef MINIZ_NO_MALLOC
#define MZ_MALLOC(x) NULL
#define MZ_FREE(x) (void)x, ((void)0)
#define MZ_REALLOC(p, x) NULL
#else
#define MZ_MALLOC(x) malloc(x)
#define MZ_FREE(x) free(x)
#define MZ_REALLOC(p, x) realloc(p, x)
#endif
#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif
#ifdef _MSC_VER
#define MZ_FORCEINLINE __forceinline
#elif defined(__GNUC__)
#define MZ_FORCEINLINE inline __attribute__((__always_inline__))
#else
#define MZ_FORCEINLINE inline
#endif
#ifdef __cplusplus
extern "C" {
#endif
// ------------------- zlib-style API's
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
{
mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
if (!ptr) return MZ_ADLER32_INIT;
while (buf_len) {
for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
return (s2 << 16) + s1;
}
// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed"
mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
{
static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
mz_uint32 crcu32 = (mz_uint32)crc;
if (!ptr) return MZ_CRC32_INIT;
crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
return ~crcu32;
}
void mz_free(void *p)
{
MZ_FREE(p);
}
#ifndef MINIZ_NO_ZLIB_APIS
static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); }
static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); }
static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); }
const char *mz_version(void)
{
return MZ_VERSION;
}
int mz_deflateInit(mz_streamp pStream, int level)
{
return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
}
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
{
tdefl_compressor *pComp;
mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
if (!pStream) return MZ_STREAM_ERROR;
if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR;
pStream->data_type = 0;
pStream->adler = MZ_ADLER32_INIT;
pStream->msg = NULL;
pStream->reserved = 0;
pStream->total_in = 0;
pStream->total_out = 0;
if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
if (!pStream->zfree) pStream->zfree = def_free_func;
pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
if (!pComp)
return MZ_MEM_ERROR;
pStream->state = (struct mz_internal_state *)pComp;
if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
{
mz_deflateEnd(pStream);
return MZ_PARAM_ERROR;
}
return MZ_OK;
}
int mz_deflateReset(mz_streamp pStream)
{
if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR;
pStream->total_in = pStream->total_out = 0;
tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags);
return MZ_OK;
}
int mz_deflate(mz_streamp pStream, int flush)
{
size_t in_bytes, out_bytes;
mz_ulong orig_total_in, orig_total_out;
int mz_status = MZ_OK;
if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR;
if (!pStream->avail_out) return MZ_BUF_ERROR;
if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
orig_total_in = pStream->total_in; orig_total_out = pStream->total_out;
for ( ; ; )
{
tdefl_status defl_status;
in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state);
pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes;
pStream->total_out += (mz_uint)out_bytes;
if (defl_status < 0)
{
mz_status = MZ_STREAM_ERROR;
break;
}
else if (defl_status == TDEFL_STATUS_DONE)
{
mz_status = MZ_STREAM_END;
break;
}
else if (!pStream->avail_out)
break;
else if ((!pStream->avail_in) && (flush != MZ_FINISH))
{
if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
break;
return MZ_BUF_ERROR; // Can't make forward progress without some input.
}
}
return mz_status;
}
int mz_deflateEnd(mz_streamp pStream)
{
if (!pStream) return MZ_STREAM_ERROR;
if (pStream->state)
{
pStream->zfree(pStream->opaque, pStream->state);
pStream->state = NULL;
}
return MZ_OK;
}
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
{
(void)pStream;
// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
}
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
{
int status;
mz_stream stream;
memset(&stream, 0, sizeof(stream));
// In case mz_ulong is 64-bits (argh I hate longs).
if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
stream.next_in = pSource;
stream.avail_in = (mz_uint32)source_len;
stream.next_out = pDest;
stream.avail_out = (mz_uint32)*pDest_len;
status = mz_deflateInit(&stream, level);
if (status != MZ_OK) return status;
status = mz_deflate(&stream, MZ_FINISH);
if (status != MZ_STREAM_END)
{
mz_deflateEnd(&stream);
return (status == MZ_OK) ? MZ_BUF_ERROR : status;
}
*pDest_len = stream.total_out;
return mz_deflateEnd(&stream);
}
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
{
return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
}
mz_ulong mz_compressBound(mz_ulong source_len)
{
return mz_deflateBound(NULL, source_len);
}
typedef struct
{
tinfl_decompressor m_decomp;
mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
tinfl_status m_last_status;
} inflate_state;
int mz_inflateInit2(mz_streamp pStream, int window_bits)
{
inflate_state *pDecomp;
if (!pStream) return MZ_STREAM_ERROR;
if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
pStream->data_type = 0;
pStream->adler = 0;
pStream->msg = NULL;
pStream->total_in = 0;
pStream->total_out = 0;
pStream->reserved = 0;
if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
if (!pStream->zfree) pStream->zfree = def_free_func;
pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
if (!pDecomp) return MZ_MEM_ERROR;
pStream->state = (struct mz_internal_state *)pDecomp;
tinfl_init(&pDecomp->m_decomp);
pDecomp->m_dict_ofs = 0;
pDecomp->m_dict_avail = 0;
pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
pDecomp->m_first_call = 1;
pDecomp->m_has_flushed = 0;
pDecomp->m_window_bits = window_bits;
return MZ_OK;
}
int mz_inflateInit(mz_streamp pStream)
{
return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
}
int mz_inflate(mz_streamp pStream, int flush)
{
inflate_state* pState;
mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
size_t in_bytes, out_bytes, orig_avail_in;
tinfl_status status;
if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState = (inflate_state*)pStream->state;
if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
orig_avail_in = pStream->avail_in;
first_call = pState->m_first_call; pState->m_first_call = 0;
if (pState->m_last_status < 0) return MZ_DATA_ERROR;
if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState->m_has_flushed |= (flush == MZ_FINISH);
if ((flush == MZ_FINISH) && (first_call))
{
// MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file.
decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
if (status < 0)
return MZ_DATA_ERROR;
else if (status != TINFL_STATUS_DONE)
{
pState->m_last_status = TINFL_STATUS_FAILED;
return MZ_BUF_ERROR;
}
return MZ_STREAM_END;
}
// flush != MZ_FINISH then we must assume there's more input.
if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
if (pState->m_dict_avail)
{
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
for ( ; ; )
{
in_bytes = pStream->avail_in;
out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pState->m_dict_avail = (mz_uint)out_bytes;
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
if (status < 0)
return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH.
else if (flush == MZ_FINISH)
{
// The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH.
if (status == TINFL_STATUS_DONE)
return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
// status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong.
else if (!pStream->avail_out)
return MZ_BUF_ERROR;
}
else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
break;
}
return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
int mz_inflateEnd(mz_streamp pStream)
{
if (!pStream)
return MZ_STREAM_ERROR;
if (pStream->state)
{
pStream->zfree(pStream->opaque, pStream->state);
pStream->state = NULL;
}
return MZ_OK;
}
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
{
mz_stream stream;
int status;
memset(&stream, 0, sizeof(stream));
// In case mz_ulong is 64-bits (argh I hate longs).
if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
stream.next_in = pSource;
stream.avail_in = (mz_uint32)source_len;
stream.next_out = pDest;
stream.avail_out = (mz_uint32)*pDest_len;
status = mz_inflateInit(&stream);
if (status != MZ_OK)
return status;
status = mz_inflate(&stream, MZ_FINISH);
if (status != MZ_STREAM_END)
{
mz_inflateEnd(&stream);
return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
}
*pDest_len = stream.total_out;
return mz_inflateEnd(&stream);
}
const char *mz_error(int err)
{
static struct { int m_err; const char *m_pDesc; } s_error_descs[] =
{
{ MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" },
{ MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
};
mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc;
return NULL;
}
#endif //MINIZ_NO_ZLIB_APIS
// ------------------- Low-level Decompression (completely independent from all compression API's)
#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)
#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
#define TINFL_CR_FINISH }
// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
#define TINFL_GET_BYTE(state_index, c) do { \
if (pIn_buf_cur >= pIn_buf_end) { \
for ( ; ; ) { \
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
if (pIn_buf_cur < pIn_buf_end) { \
c = *pIn_buf_cur++; \
break; \
} \
} else { \
c = 0; \
break; \
} \
} \
} else c = *pIn_buf_cur++; } MZ_MACRO_END
#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
// bit buffer contains >=15 bits (deflate's max. Huffman code size).
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
do { \
temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
if (temp >= 0) { \
code_len = temp >> 9; \
if ((code_len) && (num_bits >= code_len)) \
break; \
} else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
code_len = TINFL_FAST_LOOKUP_BITS; \
do { \
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
} TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
} while (num_bits < 15);
// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
// The slow path is only executed at the very end of the input buffer.
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
int temp; mz_uint code_len, c; \
if (num_bits < 15) { \
if ((pIn_buf_end - pIn_buf_cur) < 2) { \
TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
} else { \
bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
} \
} \
if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
code_len = temp >> 9, temp &= 511; \
else { \
code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
} sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
static const int s_min_table_sizes[3] = { 257, 1, 4 };
tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
// Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
TINFL_CR_BEGIN
bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
}
do
{
TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
if (r->m_type == 0)
{
TINFL_SKIP_BITS(5, num_bits & 7);
for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
while ((counter) && (num_bits))
{
TINFL_GET_BITS(51, dist, 8);
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)dist;
counter--;
}
while (counter)
{
size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
while (pIn_buf_cur >= pIn_buf_end)
{
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
{
TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
}
else
{
TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
}
}
n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
}
}
else if (r->m_type == 3)
{
TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
}
else
{
if (r->m_type == 1)
{
mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
}
else
{
for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
r->m_table_sizes[2] = 19;
}
for ( ; (int)r->m_type >= 0; r->m_type--)
{
int tree_next, tree_cur; tinfl_huff_table *pTable;
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
if ((65536 != total) && (used_syms > 1))
{
TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
}
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
{
mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
{
tree_cur -= ((rev_code >>= 1) & 1);
if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
}
tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
}
if (r->m_type == 2)
{
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
{
mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
if ((dist == 16) && (!counter))
{
TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
}
num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
}
if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
{
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
}
TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
}
}
for ( ; ; )
{
mz_uint8 *pSrc;
for ( ; ; )
{
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
{
TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
if (counter >= 256)
break;
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)counter;
}
else
{
int sym2; mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
#else
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
if (counter & 256)
break;
#if !TINFL_USE_64BIT_BITBUF
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
bit_buf >>= code_len; num_bits -= code_len;
pOut_buf_cur[0] = (mz_uint8)counter;
if (sym2 & 256)
{
pOut_buf_cur++;
counter = sym2;
break;
}
pOut_buf_cur[1] = (mz_uint8)sym2;
pOut_buf_cur += 2;
}
}
if ((counter &= 511) == 256) break;
num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
{
TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
}
pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
{
while (counter--)
{
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
}
continue;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
else if ((counter >= 9) && (counter <= dist))
{
const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
do
{
((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
pOut_buf_cur += 8;
} while ((pSrc += 8) < pSrc_end);
if ((counter &= 7) < 3)
{
if (counter)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
continue;
}
}
#endif
do
{
pOut_buf_cur[0] = pSrc[0];
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur[2] = pSrc[2];
pOut_buf_cur += 3; pSrc += 3;
} while ((int)(counter -= 3) > 2);
if ((int)counter > 0)
{
pOut_buf_cur[0] = pSrc[0];
if ((int)counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
}
}
} while (!(r->m_final & 1));
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
}
TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
TINFL_CR_FINISH
common_exit:
r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
while (buf_len)
{
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
{
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
// Higher level helper functions.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
*pOut_len = 0;
tinfl_init(&decomp);
for ( ; ; )
{
size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
(flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
{
MZ_FREE(pBuf); *pOut_len = 0; return NULL;
}
src_buf_ofs += src_buf_size;
*pOut_len += dst_buf_size;
if (status == TINFL_STATUS_DONE) break;
new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
if (!pNew_buf)
{
MZ_FREE(pBuf); *pOut_len = 0; return NULL;
}
pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
}
return pBuf;
}
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
}
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
int result = 0;
tinfl_decompressor decomp;
mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
if (!pDict)
return TINFL_STATUS_FAILED;
tinfl_init(&decomp);
for ( ; ; )
{
size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
(flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
in_buf_ofs += in_buf_size;
if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
break;
if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
{
result = (status == TINFL_STATUS_DONE);
break;
}
dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
}
MZ_FREE(pDict);
*pIn_buf_size = in_buf_ofs;
return result;
}
// ------------------- Low-level Compression (independent from all decompression API's)
// Purposely making these tables static for faster init and thread safety.
static const mz_uint16 s_tdefl_len_sym[256] = {
257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272,
273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276,
277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,
279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,
282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,
283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,
284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 };
static const mz_uint8 s_tdefl_len_extra[256] = {
0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 };
static const mz_uint8 s_tdefl_small_dist_sym[512] = {
0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 };
static const mz_uint8 s_tdefl_small_dist_extra[512] = {
0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7 };
static const mz_uint8 s_tdefl_large_dist_sym[128] = {
0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,
26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,
28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 };
static const mz_uint8 s_tdefl_large_dist_extra[128] = {
0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 };
// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.
typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq;
static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1)
{
mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist);
for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; }
while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
{
const mz_uint32* pHist = &hist[pass << 8];
mz_uint offsets[256], cur_ofs = 0;
for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
{ tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; }
}
return pCur_syms;
}
// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
{
int root, leaf, next, avbl, used, dpth;
if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
A[0].m_key += A[1].m_key; root = 0; leaf = 2;
for (next=1; next < n-1; next++)
{
if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key;
if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
}
A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
avbl = 1; used = dpth = 0; root = n-2; next = n-1;
while (avbl>0)
{
while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; }
avbl = 2*used; dpth++; used = 0;
}
}
// Limits canonical Huffman code table's max code size.
enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
{
int i; mz_uint32 total = 0; if (code_list_len <= 1) return;
for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
while (total != (1UL << max_code_size))
{
pNum_codes[max_code_size]--;
for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
total--;
}
}
static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
{
int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes);
if (static_table)
{
for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++;
}
else
{
tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
int num_used_syms = 0;
const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; }
pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++;
tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
}
next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1);
for (i = 0; i < table_len; i++)
{
mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue;
code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1);
d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
}
}
#define TDEFL_PUT_BITS(b, l) do { \
mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \
d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \
while (d->m_bits_in >= 8) { \
if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
*d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
d->m_bit_buffer >>= 8; \
d->m_bits_in -= 8; \
} \
} MZ_MACRO_END
#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \
if (rle_repeat_count < 3) { \
d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
} else { \
d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
} rle_repeat_count = 0; } }
#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \
if (rle_z_count < 3) { \
d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \
} else if (rle_z_count <= 10) { \
d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
} else { \
d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
} rle_z_count = 0; } }
static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
static void tdefl_start_dynamic_block(tdefl_compressor *d)
{
int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
d->m_huff_count[0][256] = 1;
tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;
memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0;
memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
for (i = 0; i < total_code_sizes_to_pack; i++)
{
mz_uint8 code_size = code_sizes_to_pack[i];
if (!code_size)
{
TDEFL_RLE_PREV_CODE_SIZE();
if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); }
}
else
{
TDEFL_RLE_ZERO_CODE_SIZE();
if (code_size != prev_code_size)
{
TDEFL_RLE_PREV_CODE_SIZE();
d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size;
}
else if (++rle_repeat_count == 6)
{
TDEFL_RLE_PREV_CODE_SIZE();
}
}
prev_code_size = code_size;
}
if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); }
tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
TDEFL_PUT_BITS(2, 2);
TDEFL_PUT_BITS(num_lit_codes - 257, 5);
TDEFL_PUT_BITS(num_dist_codes - 1, 5);
for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break;
num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; )
{
mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
}
}
static void tdefl_start_static_block(tdefl_compressor *d)
{
mz_uint i;
mz_uint8 *p = &d->m_huff_code_sizes[0][0];
for (i = 0; i <= 143; ++i) *p++ = 8;
for ( ; i <= 255; ++i) *p++ = 9;
for ( ; i <= 279; ++i) *p++ = 7;
for ( ; i <= 287; ++i) *p++ = 8;
memset(d->m_huff_code_sizes[1], 5, 32);
tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
TDEFL_PUT_BITS(1, 2);
}
static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
{
mz_uint flags;
mz_uint8 *pLZ_codes;
mz_uint8 *pOutput_buf = d->m_pOutput_buf;
mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
mz_uint64 bit_buffer = d->m_bit_buffer;
mz_uint bits_in = d->m_bits_in;
#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); }
flags = 1;
for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
{
if (flags == 1)
flags = *pLZ_codes++ | 0x100;
if (flags & 1)
{
mz_uint s0, s1, n0, n1, sym, num_extra_bits;
mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3;
MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
// This sequence coaxes MSVC into using cmov's vs. jmp's.
s0 = s_tdefl_small_dist_sym[match_dist & 511];
n0 = s_tdefl_small_dist_extra[match_dist & 511];
s1 = s_tdefl_large_dist_sym[match_dist >> 8];
n1 = s_tdefl_large_dist_extra[match_dist >> 8];
sym = (match_dist < 512) ? s0 : s1;
num_extra_bits = (match_dist < 512) ? n0 : n1;
MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
}
else
{
mz_uint lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
{
flags >>= 1;
lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
{
flags >>= 1;
lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
}
}
}
if (pOutput_buf >= d->m_pOutput_buf_end)
return MZ_FALSE;
*(mz_uint64*)pOutput_buf = bit_buffer;
pOutput_buf += (bits_in >> 3);
bit_buffer >>= (bits_in & ~7);
bits_in &= 7;
}
#undef TDEFL_PUT_BITS_FAST
d->m_pOutput_buf = pOutput_buf;
d->m_bits_in = 0;
d->m_bit_buffer = 0;
while (bits_in)
{
mz_uint32 n = MZ_MIN(bits_in, 16);
TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
bit_buffer >>= n;
bits_in -= n;
}
TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
return (d->m_pOutput_buf < d->m_pOutput_buf_end);
}
#else
static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
{
mz_uint flags;
mz_uint8 *pLZ_codes;
flags = 1;
for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
{
if (flags == 1)
flags = *pLZ_codes++ | 0x100;
if (flags & 1)
{
mz_uint sym, num_extra_bits;
mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3;
MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
if (match_dist < 512)
{
sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist];
}
else
{
sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
}
MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
}
else
{
mz_uint lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
}
}
TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
return (d->m_pOutput_buf < d->m_pOutput_buf_end);
}
#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
{
if (static_block)
tdefl_start_static_block(d);
else
tdefl_start_dynamic_block(d);
return tdefl_compress_lz_codes(d);
}
static int tdefl_flush_block(tdefl_compressor *d, int flush)
{
mz_uint saved_bit_buf, saved_bits_in;
mz_uint8 *pSaved_output_buf;
mz_bool comp_block_succeeded = MZ_FALSE;
int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
d->m_pOutput_buf = pOutput_buf_start;
d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
MZ_ASSERT(!d->m_output_flush_remaining);
d->m_output_flush_ofs = 0;
d->m_output_flush_remaining = 0;
*d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
{
TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8);
}
TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in;
if (!use_raw_block)
comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
// If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.
if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) )
{
mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
TDEFL_PUT_BITS(0, 2);
if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
{
TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
}
for (i = 0; i < d->m_total_lz_bytes; ++i)
{
TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
}
}
// Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.
else if (!comp_block_succeeded)
{
d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
tdefl_compress_block(d, MZ_TRUE);
}
if (flush)
{
if (flush == TDEFL_FINISH)
{
if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } }
}
else
{
mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); }
}
}
MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++;
if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
{
if (d->m_pPut_buf_func)
{
*d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
}
else if (pOutput_buf_start == d->m_output_buf)
{
int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
d->m_out_buf_ofs += bytes_to_copy;
if ((n -= bytes_to_copy) != 0)
{
d->m_output_flush_ofs = bytes_to_copy;
d->m_output_flush_remaining = n;
}
}
else
{
d->m_out_buf_ofs += n;
}
}
return d->m_output_flush_remaining;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p)
static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
{
mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q;
mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);
MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
for ( ; ; )
{
for ( ; ; )
{
if (--num_probes_left == 0) return;
#define TDEFL_PROBE \
next_probe_pos = d->m_next[probe_pos]; \
if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break;
TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
}
if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32;
do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
if (!probe_len)
{
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
}
else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
{
*pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break;
c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
}
}
}
#else
static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
{
mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
const mz_uint8 *s = d->m_dict + pos, *p, *q;
mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
for ( ; ; )
{
for ( ; ; )
{
if (--num_probes_left == 0) return;
#define TDEFL_PROBE \
next_probe_pos = d->m_next[probe_pos]; \
if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
}
if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break;
if (probe_len > match_len)
{
*pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1];
}
}
}
#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
static mz_bool tdefl_compress_fast(tdefl_compressor *d)
{
// Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.
mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
{
const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
d->m_src_buf_left -= num_bytes_to_process;
lookahead_size += num_bytes_to_process;
while (num_bytes_to_process)
{
mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
d->m_pSrc += n;
dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
num_bytes_to_process -= n;
}
dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break;
while (lookahead_size >= 4)
{
mz_uint cur_match_dist, cur_match_len = 1;
mz_uint8 *pCur_dict = d->m_dict + cur_pos;
mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
mz_uint probe_pos = d->m_hash[hash];
d->m_hash[hash] = (mz_uint16)lookahead_pos;
if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
{
const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
mz_uint32 probe_len = 32;
do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
if (!probe_len)
cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)))
{
cur_match_len = 1;
*pLZ_code_buf++ = (mz_uint8)first_trigram;
*pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
d->m_huff_count[0][(mz_uint8)first_trigram]++;
}
else
{
mz_uint32 s0, s1;
cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
cur_match_dist--;
pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
*(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
pLZ_code_buf += 3;
*pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
}
}
else
{
*pLZ_code_buf++ = (mz_uint8)first_trigram;
*pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
d->m_huff_count[0][(mz_uint8)first_trigram]++;
}
if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
total_lz_bytes += cur_match_len;
lookahead_pos += cur_match_len;
dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
MZ_ASSERT(lookahead_size >= cur_match_len);
lookahead_size -= cur_match_len;
if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
{
int n;
d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
if ((n = tdefl_flush_block(d, 0)) != 0)
return (n < 0) ? MZ_FALSE : MZ_TRUE;
total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
}
}
while (lookahead_size)
{
mz_uint8 lit = d->m_dict[cur_pos];
total_lz_bytes++;
*pLZ_code_buf++ = lit;
*pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
d->m_huff_count[0][lit]++;
lookahead_pos++;
dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
lookahead_size--;
if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
{
int n;
d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
if ((n = tdefl_flush_block(d, 0)) != 0)
return (n < 0) ? MZ_FALSE : MZ_TRUE;
total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
}
}
}
d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
return MZ_TRUE;
}
#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
{
d->m_total_lz_bytes++;
*d->m_pLZ_code_buf++ = lit;
*d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
d->m_huff_count[0][lit]++;
}
static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
{
mz_uint32 s0, s1;
MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
d->m_total_lz_bytes += match_len;
d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
match_dist -= 1;
d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3;
*d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
}
static mz_bool tdefl_compress_normal(tdefl_compressor *d)
{
const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left;
tdefl_flush flush = d->m_flush;
while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
{
mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
// Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.
if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
{
mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
src_buf_left -= num_bytes_to_process;
d->m_lookahead_size += num_bytes_to_process;
while (pSrc != pSrc_end)
{
mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++;
}
}
else
{
while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
{
mz_uint8 c = *pSrc++;
mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
src_buf_left--;
d->m_dict[dst_pos] = c;
if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
{
mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
}
}
}
d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
break;
// Simple lazy/greedy parsing state machine.
len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
{
if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
{
mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; }
if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1;
}
}
else
{
tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
}
if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
{
cur_match_dist = cur_match_len = 0;
}
if (d->m_saved_match_len)
{
if (cur_match_len > d->m_saved_match_len)
{
tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
if (cur_match_len >= 128)
{
tdefl_record_match(d, cur_match_len, cur_match_dist);
d->m_saved_match_len = 0; len_to_move = cur_match_len;
}
else
{
d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
}
}
else
{
tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0;
}
}
else if (!cur_match_dist)
tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
{
tdefl_record_match(d, cur_match_len, cur_match_dist);
len_to_move = cur_match_len;
}
else
{
d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
}
// Move the lookahead forward by len_to_move bytes.
d->m_lookahead_pos += len_to_move;
MZ_ASSERT(d->m_lookahead_size >= len_to_move);
d->m_lookahead_size -= len_to_move;
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
// Check if it's time to flush the current LZ codes to the internal output buffer.
if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
{
int n;
d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
if ((n = tdefl_flush_block(d, 0)) != 0)
return (n < 0) ? MZ_FALSE : MZ_TRUE;
}
}
d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
return MZ_TRUE;
}
static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
{
if (d->m_pIn_buf_size)
{
*d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
}
if (d->m_pOut_buf_size)
{
size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
d->m_output_flush_ofs += (mz_uint)n;
d->m_output_flush_remaining -= (mz_uint)n;
d->m_out_buf_ofs += n;
*d->m_pOut_buf_size = d->m_out_buf_ofs;
}
return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
}
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
{
if (!d)
{
if (pIn_buf_size) *pIn_buf_size = 0;
if (pOut_buf_size) *pOut_buf_size = 0;
return TDEFL_STATUS_BAD_PARAM;
}
d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size;
d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size;
d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
d->m_out_buf_ofs = 0;
d->m_flush = flush;
if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
(d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) )
{
if (pIn_buf_size) *pIn_buf_size = 0;
if (pOut_buf_size) *pOut_buf_size = 0;
return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
}
d->m_wants_to_finish |= (flush == TDEFL_FINISH);
if ((d->m_output_flush_remaining) || (d->m_finished))
return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
{
if (!tdefl_compress_fast(d))
return d->m_prev_return_status;
}
else
#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
{
if (!tdefl_compress_normal(d))
return d->m_prev_return_status;
}
if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
{
if (tdefl_flush_block(d, flush) < 0)
return d->m_prev_return_status;
d->m_finished = (flush == TDEFL_FINISH);
if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; }
}
return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
}
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
{
MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
}
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user;
d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash);
d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8;
d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY;
d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1;
d->m_pIn_buf = NULL; d->m_pOut_buf = NULL;
d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL;
d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0;
memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
return TDEFL_STATUS_OKAY;
}
tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
{
return d->m_prev_return_status;
}
mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
{
return d->m_adler32;
}
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE;
pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE;
succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
MZ_FREE(pComp); return succeeded;
}
typedef struct
{
size_t m_size, m_capacity;
mz_uint8 *m_pBuf;
mz_bool m_expandable;
} tdefl_output_buffer;
static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
{
tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
size_t new_size = p->m_size + len;
if (new_size > p->m_capacity)
{
size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE;
do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity);
pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE;
p->m_pBuf = pNew_buf; p->m_capacity = new_capacity;
}
memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size;
return MZ_TRUE;
}
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
if (!pOut_len) return MZ_FALSE; else *pOut_len = 0;
out_buf.m_expandable = MZ_TRUE;
if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL;
*pOut_len = out_buf.m_size; return out_buf.m_pBuf;
}
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
if (!pOut_buf) return 0;
out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len;
if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0;
return out_buf.m_size;
}
#ifndef MINIZ_NO_ZLIB_APIS
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
{
mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES;
else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK;
else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES;
return comp_flags;
}
#endif //MINIZ_NO_ZLIB_APIS
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal)
#endif
// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299
// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck.
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
{
// Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined.
static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0;
if (!pComp) return NULL;
MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; }
// write dummy header
for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
// compress image data
tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
// write real header
*pLen_out = out_buf.m_size-41;
{
static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06};
mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0,
(mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54};
c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24);
memcpy(out_buf.m_pBuf, pnghdr, 41);
}
// write footer (IDAT CRC-32, followed by IEND chunk)
if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24);
// compute final size of file, grab compressed data buffer and return
*pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf;
}
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
{
// Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out)
return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
}
#ifdef _MSC_VER
#pragma warning (pop)
#endif
// ------------------- .ZIP archive reading
#ifndef MINIZ_NO_ARCHIVE_APIS
#ifdef MINIZ_NO_STDIO
#define MZ_FILE void *
#else
#include <stdio.h>
#include <sys/stat.h>
#if defined(_MSC_VER) || defined(__MINGW64__)
static FILE *mz_fopen(const char *pFilename, const char *pMode)
{
FILE* pFile = NULL;
fopen_s(&pFile, pFilename, pMode);
return pFile;
}
static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
{
FILE* pFile = NULL;
if (freopen_s(&pFile, pPath, pMode, pStream))
return NULL;
return pFile;
}
#ifndef MINIZ_NO_TIME
#include <sys/utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN mz_fopen
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 _ftelli64
#define MZ_FSEEK64 _fseeki64
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN mz_freopen
#define MZ_DELETE_FILE remove
#elif defined(__MINGW32__)
#ifndef MINIZ_NO_TIME
#include <sys/utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftello64
#define MZ_FSEEK64 fseeko64
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
#define MZ_DELETE_FILE remove
#elif defined(__TINYC__)
#ifndef MINIZ_NO_TIME
#include <sys/utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftell
#define MZ_FSEEK64 fseek
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
#define MZ_DELETE_FILE remove
#elif defined(__GNUC__) && _LARGEFILE64_SOURCE
#ifndef MINIZ_NO_TIME
#include <utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen64(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftello64
#define MZ_FSEEK64 fseeko64
#define MZ_FILE_STAT_STRUCT stat64
#define MZ_FILE_STAT stat64
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
#define MZ_DELETE_FILE remove
#else
#ifndef MINIZ_NO_TIME
#include <utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftello
#define MZ_FSEEK64 fseeko
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
#define MZ_DELETE_FILE remove
#endif // #ifdef _MSC_VER
#endif // #ifdef MINIZ_NO_STDIO
#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
enum
{
// ZIP archive identifiers and record sizes
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
// Central directory header record offsets
MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16,
MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
// Local directory header offsets
MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10,
MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
// End of central directory offsets
MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
};
typedef struct
{
void *m_p;
size_t m_size, m_capacity;
mz_uint m_element_size;
} mz_zip_array;
struct mz_zip_internal_state_tag
{
mz_zip_array m_central_dir;
mz_zip_array m_central_dir_offsets;
mz_zip_array m_sorted_central_dir_offsets;
MZ_FILE *m_pFile;
void *m_pMem;
size_t m_mem_size;
size_t m_mem_capacity;
};
#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
memset(pArray, 0, sizeof(mz_zip_array));
}
static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
{
void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE;
if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE;
pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
{
if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; }
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
{
if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; }
pArray->m_size = new_size;
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
{
return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
}
static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
{
size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE;
memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
return MZ_TRUE;
}
#ifndef MINIZ_NO_TIME
static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date)
{
struct tm tm;
memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
return mktime(&tm);
}
static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
{
#ifdef _MSC_VER
struct tm tm_struct;
struct tm *tm = &tm_struct;
errno_t err = localtime_s(tm, &time);
if (err)
{
*pDOS_date = 0; *pDOS_time = 0;
return;
}
#else
struct tm *tm = localtime(&time);
#endif
*pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
*pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
}
#endif
#ifndef MINIZ_NO_STDIO
static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
{
#ifdef MINIZ_NO_TIME
(void)pFilename; *pDOS_date = *pDOS_time = 0;
#else
struct MZ_FILE_STAT_STRUCT file_stat;
// On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
return MZ_FALSE;
mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
#endif // #ifdef MINIZ_NO_TIME
return MZ_TRUE;
}
#ifndef MINIZ_NO_TIME
static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time)
{
struct utimbuf t; t.actime = access_time; t.modtime = modified_time;
return !utime(pFilename, &t);
}
#endif // #ifndef MINIZ_NO_TIME
#endif // #ifndef MINIZ_NO_STDIO
static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags)
{
(void)flags;
if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
return MZ_FALSE;
if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
pZip->m_zip_mode = MZ_ZIP_MODE_READING;
pZip->m_archive_size = 0;
pZip->m_central_directory_file_ofs = 0;
pZip->m_total_files = 0;
if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
return MZ_FALSE;
memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
{
const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
mz_uint8 l = 0, r = 0;
pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
pE = pL + MZ_MIN(l_len, r_len);
while (pL < pE)
{
if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
break;
pL++; pR++;
}
return (pL == pE) ? (l_len < r_len) : (l < r);
}
#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END
// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
{
mz_zip_internal_state *pState = pZip->m_pState;
const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
const mz_zip_array *pCentral_dir = &pState->m_central_dir;
mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
const int size = pZip->m_total_files;
int start = (size - 2) >> 1, end;
while (start >= 0)
{
int child, root = start;
for ( ; ; )
{
if ((child = (root << 1) + 1) >= size)
break;
child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
break;
MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
}
start--;
}
end = size - 1;
while (end > 0)
{
int child, root = 0;
MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
for ( ; ; )
{
if ((child = (root << 1) + 1) >= end)
break;
child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
break;
MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
}
end--;
}
}
static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags)
{
mz_uint cdir_size, num_this_disk, cdir_disk_index;
mz_uint64 cdir_ofs;
mz_int64 cur_file_ofs;
const mz_uint8 *p;
mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
mz_bool zip_signature_found = 0;
// Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE;
// Find the end of central directory record by scanning the file from the end towards the beginning.
cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
for ( ; ; )
{
int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
return MZ_FALSE;
for (i = n - 4; i >= 0; --i)
{
if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
{
// Read and verify the end of central directory record.
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs + i, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
continue;
if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
continue;
zip_signature_found = 1;
break;
}
}
if (zip_signature_found)
{
cur_file_ofs += i;
break;
}
if ((!cur_file_ofs) || (cur_file_ofs < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))
return MZ_FALSE;
cur_file_ofs = MZ_MAX(cur_file_ofs - (mz_int64)(sizeof(buf_u32) - 3), 0);
}
num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
return MZ_FALSE;
if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE;
cdir_ofs = cur_file_ofs - cdir_size;
if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
return MZ_FALSE;
pZip->m_central_directory_file_ofs = cdir_ofs;
pZip->m_archive_file_ofs = cdir_ofs - MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
if (pZip->m_archive_file_ofs > pZip->m_archive_size)
return MZ_FALSE;
if (pZip->m_total_files)
{
mz_uint i, n;
// Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
(!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
return MZ_FALSE;
if (sort_central_dir)
{
if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
return MZ_FALSE;
}
if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
return MZ_FALSE;
// Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
{
mz_uint total_header_size, comp_size, decomp_size, disk_index;
if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
return MZ_FALSE;
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
if (sort_central_dir)
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
return MZ_FALSE;
disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
if ((disk_index != num_this_disk) && (disk_index != 1))
return MZ_FALSE;
if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
return MZ_FALSE;
if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
return MZ_FALSE;
n -= total_header_size; p += total_header_size;
}
}
if (sort_central_dir)
mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
return MZ_TRUE;
}
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags)
{
if ((!pZip) || (!pZip->m_pRead))
return MZ_FALSE;
if (!mz_zip_reader_init_internal(pZip, flags))
return MZ_FALSE;
pZip->m_archive_size = size;
if (!mz_zip_reader_read_central_dir(pZip, flags))
{
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
return MZ_TRUE;
}
static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
return s;
}
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags)
{
if (!mz_zip_reader_init_internal(pZip, flags))
return MZ_FALSE;
pZip->m_archive_size = size;
pZip->m_pRead = mz_zip_mem_read_func;
pZip->m_pIO_opaque = pZip;
#ifdef __cplusplus
pZip->m_pState->m_pMem = const_cast<void *>(pMem);
#else
pZip->m_pState->m_pMem = (void *)pMem;
#endif
pZip->m_pState->m_mem_size = size;
if (!mz_zip_reader_read_central_dir(pZip, flags))
{
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
return 0;
return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
}
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
{
mz_uint64 file_size;
MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
if (!pFile)
return MZ_FALSE;
if (MZ_FSEEK64(pFile, 0, SEEK_END))
{
MZ_FCLOSE(pFile);
return MZ_FALSE;
}
file_size = MZ_FTELL64(pFile);
if (!mz_zip_reader_init_internal(pZip, flags))
{
MZ_FCLOSE(pFile);
return MZ_FALSE;
}
pZip->m_pRead = mz_zip_file_read_func;
pZip->m_pIO_opaque = pZip;
pZip->m_pState->m_pFile = pFile;
pZip->m_archive_size = file_size;
if (!mz_zip_reader_read_central_dir(pZip, flags))
{
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
{
return pZip ? pZip->m_total_files : 0;
}
static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
{
if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return NULL;
return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
}
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
{
mz_uint m_bit_flag;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if (!p)
return MZ_FALSE;
m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
return (m_bit_flag & 1);
}
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
{
mz_uint filename_len, external_attr;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if (!p)
return MZ_FALSE;
// First see if the filename ends with a '/' character.
filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
if (filename_len)
{
if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
return MZ_TRUE;
}
// Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
// Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
// FIXME: Remove this check? Is it necessary - we already check the filename.
external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
if ((external_attr & 0x10) != 0)
return MZ_TRUE;
return MZ_FALSE;
}
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
{
mz_uint n;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if ((!p) || (!pStat))
return MZ_FALSE;
// Unpack the central directory record.
pStat->m_file_index = file_index;
pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
#ifndef MINIZ_NO_TIME
pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
#endif
pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
// Copy as much of the filename and comment as possible.
n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';
n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
pStat->m_comment_size = n;
memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';
return MZ_TRUE;
}
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
{
mz_uint n;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
if (filename_buf_size)
{
n = MZ_MIN(n, filename_buf_size - 1);
memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
pFilename[n] = '\0';
}
return n + 1;
}
static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
{
mz_uint i;
if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
return 0 == memcmp(pA, pB, len);
for (i = 0; i < len; ++i)
if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
return MZ_FALSE;
return MZ_TRUE;
}
static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
{
const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
mz_uint8 l = 0, r = 0;
pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
pE = pL + MZ_MIN(l_len, r_len);
while (pL < pE)
{
if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
break;
pL++; pR++;
}
return (pL == pE) ? (int)(l_len - r_len) : (l - r);
}
static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename)
{
mz_zip_internal_state *pState = pZip->m_pState;
const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
const mz_zip_array *pCentral_dir = &pState->m_central_dir;
mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
const int size = pZip->m_total_files;
const mz_uint filename_len = (mz_uint)strlen(pFilename);
int l = 0, h = size - 1;
while (l <= h)
{
int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
if (!comp)
return file_index;
else if (comp < 0)
l = m + 1;
else
h = m - 1;
}
return -1;
}
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
{
mz_uint file_index; size_t name_len, comment_len;
if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return -1;
if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
return mz_zip_reader_locate_file_binary_search(pZip, pName);
name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
for (file_index = 0; file_index < pZip->m_total_files; file_index++)
{
const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
if (filename_len < name_len)
continue;
if (comment_len)
{
mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
const char *pFile_comment = pFilename + filename_len + file_extra_len;
if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
continue;
}
if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
{
int ofs = filename_len - 1;
do
{
if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
break;
} while (--ofs >= 0);
ofs++;
pFilename += ofs; filename_len -= ofs;
}
if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
return file_index;
}
return -1;
}
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
{
int status = TINFL_STATUS_DONE;
mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
mz_zip_archive_file_stat file_stat;
void *pRead_buf;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
tinfl_decompressor inflator;
if ((buf_size) && (!pBuf))
return MZ_FALSE;
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
return MZ_FALSE;
// Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
if (!file_stat.m_comp_size)
return MZ_TRUE;
// Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
// I'm torn how to handle this case - should it fail instead?
if (mz_zip_reader_is_file_a_directory(pZip, file_index))
return MZ_TRUE;
// Encryption and patch files are not supported.
if (file_stat.m_bit_flag & (1 | 32))
return MZ_FALSE;
// This function only supports stored and deflate.
if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
return MZ_FALSE;
// Ensure supplied output buffer is large enough.
needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
if (buf_size < needed_size)
return MZ_FALSE;
// Read and parse the local directory entry.
cur_file_ofs = pZip->m_archive_file_ofs + file_stat.m_local_header_ofs;
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return MZ_FALSE;
cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
return MZ_FALSE;
if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
{
// The file is stored or the caller has requested the compressed data.
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
return MZ_FALSE;
return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
}
// Decompress the file either directly from memory or from a file input buffer.
tinfl_init(&inflator);
if (pZip->m_pState->m_pMem)
{
// Read directly from the archive in memory.
pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
read_buf_size = read_buf_avail = file_stat.m_comp_size;
comp_remaining = 0;
}
else if (pUser_read_buf)
{
// Use a user provided read buffer.
if (!user_read_buf_size)
return MZ_FALSE;
pRead_buf = (mz_uint8 *)pUser_read_buf;
read_buf_size = user_read_buf_size;
read_buf_avail = 0;
comp_remaining = file_stat.m_comp_size;
}
else
{
// Temporarily allocate a read buffer.
read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
#ifdef _MSC_VER
if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
#else
if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
#endif
return MZ_FALSE;
if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
return MZ_FALSE;
read_buf_avail = 0;
comp_remaining = file_stat.m_comp_size;
}
do
{
size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
{
read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
cur_file_ofs += read_buf_avail;
comp_remaining -= read_buf_avail;
read_buf_ofs = 0;
}
in_buf_size = (size_t)read_buf_avail;
status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
read_buf_avail -= in_buf_size;
read_buf_ofs += in_buf_size;
out_buf_ofs += out_buf_size;
} while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
if (status == TINFL_STATUS_DONE)
{
// Make sure the entire file was decompressed, and check its CRC.
if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
status = TINFL_STATUS_FAILED;
}
if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
return status == TINFL_STATUS_DONE;
}
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
{
int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
if (file_index < 0)
return MZ_FALSE;
return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
}
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
{
return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
}
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
{
return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
}
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
{
mz_uint64 comp_size, uncomp_size, alloc_size;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
void *pBuf;
if (pSize)
*pSize = 0;
if (!p)
return NULL;
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
#ifdef _MSC_VER
if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
#else
if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
#endif
return NULL;
if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
return NULL;
if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return NULL;
}
if (pSize) *pSize = (size_t)alloc_size;
return pBuf;
}
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
{
int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
if (file_index < 0)
{
if (pSize) *pSize = 0;
return MZ_FALSE;
}
return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
}
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
{
int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT;
mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
mz_zip_archive_file_stat file_stat;
void *pRead_buf = NULL; void *pWrite_buf = NULL;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
return MZ_FALSE;
// Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
if (!file_stat.m_comp_size)
return MZ_TRUE;
// Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
// I'm torn how to handle this case - should it fail instead?
if (mz_zip_reader_is_file_a_directory(pZip, file_index))
return MZ_TRUE;
// Encryption and patch files are not supported.
if (file_stat.m_bit_flag & (1 | 32))
return MZ_FALSE;
// This function only supports stored and deflate.
if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
return MZ_FALSE;
// Read and parse the local directory entry.
cur_file_ofs = file_stat.m_local_header_ofs;
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return MZ_FALSE;
cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
return MZ_FALSE;
// Decompress the file either directly from memory or from a file input buffer.
if (pZip->m_pState->m_pMem)
{
pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
read_buf_size = read_buf_avail = file_stat.m_comp_size;
comp_remaining = 0;
}
else
{
read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
return MZ_FALSE;
read_buf_avail = 0;
comp_remaining = file_stat.m_comp_size;
}
if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
{
// The file is stored or the caller has requested the compressed data.
if (pZip->m_pState->m_pMem)
{
#ifdef _MSC_VER
if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
#else
if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
#endif
return MZ_FALSE;
if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
status = TINFL_STATUS_FAILED;
else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
cur_file_ofs += file_stat.m_comp_size;
out_buf_ofs += file_stat.m_comp_size;
comp_remaining = 0;
}
else
{
while (comp_remaining)
{
read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
cur_file_ofs += read_buf_avail;
out_buf_ofs += read_buf_avail;
comp_remaining -= read_buf_avail;
}
}
}
else
{
tinfl_decompressor inflator;
tinfl_init(&inflator);
if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
status = TINFL_STATUS_FAILED;
else
{
do
{
mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
{
read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
cur_file_ofs += read_buf_avail;
comp_remaining -= read_buf_avail;
read_buf_ofs = 0;
}
in_buf_size = (size_t)read_buf_avail;
status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
read_buf_avail -= in_buf_size;
read_buf_ofs += in_buf_size;
if (out_buf_size)
{
if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
{
status = TINFL_STATUS_FAILED;
break;
}
file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
{
status = TINFL_STATUS_FAILED;
break;
}
}
} while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
}
}
if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
{
// Make sure the entire file was decompressed, and check its CRC.
if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32))
status = TINFL_STATUS_FAILED;
}
if (!pZip->m_pState->m_pMem)
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
if (pWrite_buf)
pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
return status == TINFL_STATUS_DONE;
}
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
{
int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
if (file_index < 0)
return MZ_FALSE;
return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
}
#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
{
(void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque);
}
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
{
mz_bool status;
mz_zip_archive_file_stat file_stat;
MZ_FILE *pFile;
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
return MZ_FALSE;
pFile = MZ_FOPEN(pDst_filename, "wb");
if (!pFile)
return MZ_FALSE;
status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
if (MZ_FCLOSE(pFile) == EOF)
return MZ_FALSE;
#ifndef MINIZ_NO_TIME
if (status)
mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
#endif
return status;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
{
if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return MZ_FALSE;
if (pZip->m_pState)
{
mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
mz_zip_array_clear(pZip, &pState->m_central_dir);
mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
#ifndef MINIZ_NO_STDIO
if (pState->m_pFile)
{
MZ_FCLOSE(pState->m_pFile);
pState->m_pFile = NULL;
}
#endif // #ifndef MINIZ_NO_STDIO
pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
}
pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
{
int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags);
if (file_index < 0)
return MZ_FALSE;
return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
}
#endif
// ------------------- .ZIP archive writing
#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); }
static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); }
#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
{
if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
return MZ_FALSE;
if (pZip->m_file_offset_alignment)
{
// Ensure user specified file offset alignment is a power of 2.
if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
return MZ_FALSE;
}
if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
pZip->m_archive_size = existing_size;
pZip->m_central_directory_file_ofs = 0;
pZip->m_total_files = 0;
if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
return MZ_FALSE;
memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
return MZ_TRUE;
}
static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
mz_zip_internal_state *pState = pZip->m_pState;
mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
#ifdef _MSC_VER
if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
#else
if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
#endif
return 0;
if (new_size > pState->m_mem_capacity)
{
void *pNew_block;
size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2;
if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
return 0;
pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity;
}
memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
pState->m_mem_size = (size_t)new_size;
return n;
}
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
{
pZip->m_pWrite = mz_zip_heap_write_func;
pZip->m_pIO_opaque = pZip;
if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
return MZ_FALSE;
if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
{
if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
{
mz_zip_writer_end(pZip);
return MZ_FALSE;
}
pZip->m_pState->m_mem_capacity = initial_allocation_size;
}
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
return 0;
return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
}
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
{
MZ_FILE *pFile;
pZip->m_pWrite = mz_zip_file_write_func;
pZip->m_pIO_opaque = pZip;
if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
return MZ_FALSE;
if (NULL == (pFile = MZ_FOPEN(pFilename, "wb")))
{
mz_zip_writer_end(pZip);
return MZ_FALSE;
}
pZip->m_pState->m_pFile = pFile;
if (size_to_reserve_at_beginning)
{
mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf);
do
{
size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
{
mz_zip_writer_end(pZip);
return MZ_FALSE;
}
cur_ofs += n; size_to_reserve_at_beginning -= n;
} while (size_to_reserve_at_beginning);
}
return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
{
mz_zip_internal_state *pState;
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return MZ_FALSE;
// No sense in trying to write to an archive that's already at the support max size
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
return MZ_FALSE;
pState = pZip->m_pState;
if (pState->m_pFile)
{
#ifdef MINIZ_NO_STDIO
pFilename; return MZ_FALSE;
#else
// Archive is being read from stdio - try to reopen as writable.
if (pZip->m_pIO_opaque != pZip)
return MZ_FALSE;
if (!pFilename)
return MZ_FALSE;
pZip->m_pWrite = mz_zip_file_write_func;
if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
{
// The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
#endif // #ifdef MINIZ_NO_STDIO
}
else if (pState->m_pMem)
{
// Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
if (pZip->m_pIO_opaque != pZip)
return MZ_FALSE;
pState->m_mem_capacity = pState->m_mem_size;
pZip->m_pWrite = mz_zip_heap_write_func;
}
// Archive is being read via a user provided read function - make sure the user has specified a write function too.
else if (!pZip->m_pWrite)
return MZ_FALSE;
// Start writing new files at the archive's current central directory location.
pZip->m_archive_size = pZip->m_central_directory_file_ofs;
pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
pZip->m_central_directory_file_ofs = 0;
return MZ_TRUE;
}
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
{
return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
}
typedef struct
{
mz_zip_archive *m_pZip;
mz_uint64 m_cur_archive_file_ofs;
mz_uint64 m_comp_size;
} mz_zip_writer_add_state;
static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser)
{
mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
return MZ_FALSE;
pState->m_cur_archive_file_ofs += len;
pState->m_comp_size += len;
return MZ_TRUE;
}
static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
{
(void)pZip;
memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
return MZ_TRUE;
}
static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
(void)pZip;
memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
return MZ_TRUE;
}
static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
mz_zip_internal_state *pState = pZip->m_pState;
mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
size_t orig_central_dir_size = pState->m_central_dir.m_size;
mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
// No zip64 support yet
if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
return MZ_FALSE;
if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
{
// Try to push the central directory array back into its original state.
mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
return MZ_FALSE;
}
return MZ_TRUE;
}
static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
{
// Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
if (*pArchive_name == '/')
return MZ_FALSE;
while (*pArchive_name)
{
if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
return MZ_FALSE;
pArchive_name++;
}
return MZ_TRUE;
}
static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
{
mz_uint32 n;
if (!pZip->m_file_offset_alignment)
return 0;
n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1);
}
static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
{
char buf[4096];
memset(buf, 0, MZ_MIN(sizeof(buf), n));
while (n)
{
mz_uint32 s = MZ_MIN(sizeof(buf), n);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
return MZ_FALSE;
cur_file_ofs += s; n -= s;
}
return MZ_TRUE;
}
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
{
mz_uint16 method = 0, dos_time = 0, dos_date = 0;
mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
size_t archive_name_size;
mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
tdefl_compressor *pComp = NULL;
mz_bool store_data_uncompressed;
mz_zip_internal_state *pState;
if ((int)level_and_flags < 0)
level_and_flags = MZ_DEFAULT_LEVEL;
level = level_and_flags & 0xF;
store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION))
return MZ_FALSE;
pState = pZip->m_pState;
if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
return MZ_FALSE;
// No zip64 support yet
if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_validate_archive_name(pArchive_name))
return MZ_FALSE;
#ifndef MINIZ_NO_TIME
{
time_t cur_time; time(&cur_time);
mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date);
}
#endif // #ifndef MINIZ_NO_TIME
archive_name_size = strlen(pArchive_name);
if (archive_name_size > 0xFFFF)
return MZ_FALSE;
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
// no zip64 support yet
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
return MZ_FALSE;
if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
{
// Set DOS Subdirectory attribute bit.
ext_attributes |= 0x10;
// Subdirectories cannot contain data.
if ((buf_size) || (uncomp_size))
return MZ_FALSE;
}
// Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
return MZ_FALSE;
if ((!store_data_uncompressed) && (buf_size))
{
if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
return MZ_FALSE;
}
if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
local_dir_header_ofs += num_alignment_padding_bytes;
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
MZ_CLEAR_OBJ(local_dir_header);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
cur_archive_file_ofs += archive_name_size;
if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
{
uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size);
uncomp_size = buf_size;
if (uncomp_size <= 3)
{
level = 0;
store_data_uncompressed = MZ_TRUE;
}
}
if (store_data_uncompressed)
{
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
cur_archive_file_ofs += buf_size;
comp_size = buf_size;
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
method = MZ_DEFLATED;
}
else if (buf_size)
{
mz_zip_writer_add_state state;
state.m_pZip = pZip;
state.m_cur_archive_file_ofs = cur_archive_file_ofs;
state.m_comp_size = 0;
if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
(tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
comp_size = state.m_comp_size;
cur_archive_file_ofs = state.m_cur_archive_file_ofs;
method = MZ_DEFLATED;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
pComp = NULL;
// no zip64 support yet
if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
return MZ_FALSE;
if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return MZ_FALSE;
if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
return MZ_FALSE;
pZip->m_total_files++;
pZip->m_archive_size = cur_archive_file_ofs;
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
{
mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
size_t archive_name_size;
mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
MZ_FILE *pSrc_file = NULL;
if ((int)level_and_flags < 0)
level_and_flags = MZ_DEFAULT_LEVEL;
level = level_and_flags & 0xF;
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
return MZ_FALSE;
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
return MZ_FALSE;
if (!mz_zip_writer_validate_archive_name(pArchive_name))
return MZ_FALSE;
archive_name_size = strlen(pArchive_name);
if (archive_name_size > 0xFFFF)
return MZ_FALSE;
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
// no zip64 support yet
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
return MZ_FALSE;
pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
if (!pSrc_file)
return MZ_FALSE;
MZ_FSEEK64(pSrc_file, 0, SEEK_END);
uncomp_size = MZ_FTELL64(pSrc_file);
MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
if (uncomp_size > 0xFFFFFFFF)
{
// No zip64 support yet
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
if (uncomp_size <= 3)
level = 0;
if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
{
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
local_dir_header_ofs += num_alignment_padding_bytes;
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
MZ_CLEAR_OBJ(local_dir_header);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
{
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
cur_archive_file_ofs += archive_name_size;
if (uncomp_size)
{
mz_uint64 uncomp_remaining = uncomp_size;
void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
if (!pRead_buf)
{
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
if (!level)
{
while (uncomp_remaining)
{
mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
uncomp_remaining -= n;
cur_archive_file_ofs += n;
}
comp_size = uncomp_size;
}
else
{
mz_bool result = MZ_FALSE;
mz_zip_writer_add_state state;
tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
if (!pComp)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
state.m_pZip = pZip;
state.m_cur_archive_file_ofs = cur_archive_file_ofs;
state.m_comp_size = 0;
if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
for ( ; ; )
{
size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
tdefl_status status;
if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
break;
uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
uncomp_remaining -= in_buf_size;
status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
if (status == TDEFL_STATUS_DONE)
{
result = MZ_TRUE;
break;
}
else if (status != TDEFL_STATUS_OKAY)
break;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
if (!result)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
comp_size = state.m_comp_size;
cur_archive_file_ofs = state.m_cur_archive_file_ofs;
method = MZ_DEFLATED;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
}
MZ_FCLOSE(pSrc_file); pSrc_file = NULL;
// no zip64 support yet
if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
return MZ_FALSE;
if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return MZ_FALSE;
if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
return MZ_FALSE;
pZip->m_total_files++;
pZip->m_archive_size = cur_archive_file_ofs;
return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index)
{
mz_uint n, bit_flags, num_alignment_padding_bytes;
mz_uint64 comp_bytes_remaining, local_dir_header_ofs;
mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
size_t orig_central_dir_size;
mz_zip_internal_state *pState;
void *pBuf; const mz_uint8 *pSrc_central_header;
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
return MZ_FALSE;
if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index)))
return MZ_FALSE;
pState = pZip->m_pState;
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
// no zip64 support yet
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
return MZ_FALSE;
cur_src_file_ofs = pSource_zip->m_archive_file_ofs + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
cur_dst_file_ofs = pZip->m_archive_size;
if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return MZ_FALSE;
cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
return MZ_FALSE;
cur_dst_file_ofs += num_alignment_padding_bytes;
local_dir_header_ofs = cur_dst_file_ofs;
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining)))))
return MZ_FALSE;
while (comp_bytes_remaining)
{
n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining);
if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
cur_src_file_ofs += n;
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
cur_dst_file_ofs += n;
comp_bytes_remaining -= n;
}
bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
if (bit_flags & 8)
{
// Copy data descriptor
if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
cur_src_file_ofs += n;
cur_dst_file_ofs += n;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
// no zip64 support yet
if (cur_dst_file_ofs > 0xFFFFFFFF)
return MZ_FALSE;
orig_central_dir_size = pState->m_central_dir.m_size;
memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
return MZ_FALSE;
n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n))
{
mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
return MZ_FALSE;
}
if (pState->m_central_dir.m_size > 0xFFFFFFFF)
return MZ_FALSE;
n = (mz_uint32)orig_central_dir_size;
if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
{
mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
return MZ_FALSE;
}
pZip->m_total_files++;
pZip->m_archive_size = cur_dst_file_ofs;
return MZ_TRUE;
}
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
{
mz_zip_internal_state *pState;
mz_uint64 central_dir_ofs, central_dir_size;
mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE];
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
return MZ_FALSE;
pState = pZip->m_pState;
// no zip64 support yet
if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
return MZ_FALSE;
central_dir_ofs = 0;
central_dir_size = 0;
if (pZip->m_total_files)
{
// Write central directory
central_dir_ofs = pZip->m_archive_size;
central_dir_size = pState->m_central_dir.m_size;
pZip->m_central_directory_file_ofs = central_dir_ofs;
if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
return MZ_FALSE;
pZip->m_archive_size += central_dir_size;
}
// Write end of central directory record
MZ_CLEAR_OBJ(hdr);
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size);
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr))
return MZ_FALSE;
#ifndef MINIZ_NO_STDIO
if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
return MZ_FALSE;
#endif // #ifndef MINIZ_NO_STDIO
pZip->m_archive_size += sizeof(hdr);
pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
return MZ_TRUE;
}
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize)
{
if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize))
return MZ_FALSE;
if (pZip->m_pWrite != mz_zip_heap_write_func)
return MZ_FALSE;
if (!mz_zip_writer_finalize_archive(pZip))
return MZ_FALSE;
*pBuf = pZip->m_pState->m_pMem;
*pSize = pZip->m_pState->m_mem_size;
pZip->m_pState->m_pMem = NULL;
pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
return MZ_TRUE;
}
mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
{
mz_zip_internal_state *pState;
mz_bool status = MZ_TRUE;
if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
return MZ_FALSE;
pState = pZip->m_pState;
pZip->m_pState = NULL;
mz_zip_array_clear(pZip, &pState->m_central_dir);
mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
#ifndef MINIZ_NO_STDIO
if (pState->m_pFile)
{
MZ_FCLOSE(pState->m_pFile);
pState->m_pFile = NULL;
}
#endif // #ifndef MINIZ_NO_STDIO
if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
pState->m_pMem = NULL;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
return status;
}
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
{
mz_bool status, created_new_archive = MZ_FALSE;
mz_zip_archive zip_archive;
struct MZ_FILE_STAT_STRUCT file_stat;
MZ_CLEAR_OBJ(zip_archive);
if ((int)level_and_flags < 0)
level_and_flags = MZ_DEFAULT_LEVEL;
if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
return MZ_FALSE;
if (!mz_zip_writer_validate_archive_name(pArchive_name))
return MZ_FALSE;
if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
{
// Create a new archive.
if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
return MZ_FALSE;
created_new_archive = MZ_TRUE;
}
else
{
// Append to an existing archive.
if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
return MZ_FALSE;
if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename))
{
mz_zip_reader_end(&zip_archive);
return MZ_FALSE;
}
}
status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
// Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
if (!mz_zip_writer_finalize_archive(&zip_archive))
status = MZ_FALSE;
if (!mz_zip_writer_end(&zip_archive))
status = MZ_FALSE;
if ((!status) && (created_new_archive))
{
// It's a new archive and something went wrong, so just delete it.
int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
(void)ignoredStatus;
}
return status;
}
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
{
int file_index;
mz_zip_archive zip_archive;
void *p = NULL;
if (pSize)
*pSize = 0;
if ((!pZip_filename) || (!pArchive_name))
return NULL;
MZ_CLEAR_OBJ(zip_archive);
if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
return NULL;
if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0)
p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);
mz_zip_reader_end(&zip_archive);
return p;
}
#endif // #ifndef MINIZ_NO_STDIO
#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
#ifdef __cplusplus
}
#endif
#endif // MINIZ_HEADER_FILE_ONLY
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>
*/
diff --git a/Modules/DICOM/include/mitkBaseDICOMReaderService.h b/Modules/DICOM/include/mitkBaseDICOMReaderService.h
index 75133e9064..d4b51c69ab 100644
--- a/Modules/DICOM/include/mitkBaseDICOMReaderService.h
+++ b/Modules/DICOM/include/mitkBaseDICOMReaderService.h
@@ -1,70 +1,70 @@
/*============================================================================
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 mitkBaseDICOMReaderService_h
#define mitkBaseDICOMReaderService_h
#include <mitkAbstractFileReader.h>
#include <mitkDICOMFileReader.h>
#include "MitkDICOMExports.h"
namespace mitk {
/**
Base class for service wrappers that make DICOMFileReader from
the DICOM module usable.
*/
class MITKDICOM_EXPORT BaseDICOMReaderService : public AbstractFileReader
{
public:
using AbstractFileReader::Read;
IFileReader::ConfidenceLevel GetConfidenceLevel() const override;
protected:
BaseDICOMReaderService(const std::string& description);
BaseDICOMReaderService(const mitk::CustomMimeType& customType, const std::string& description);
/** Uses this->GetRelevantFile() and this->GetReader to load the image.
* data and puts it into base data instances-*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
/** Returns the list of all DCM files that are in the same directory
* like this->GetLocalFileName().*/
mitk::StringList GetDICOMFilesInSameDirectory() const;
/** Returns the reader instance that should be used. The decision may be based
* one the passed list of relevant files.*/
virtual mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const = 0;
void SetOnlyRegardOwnSeries(bool);
bool GetOnlyRegardOwnSeries() const;
private:
- /** Flags that constrols if the read() operation should only regard DICOM files of the same series
+ /** Flags that controls if the read() operation should only regard DICOM files of the same series
if the specified GetLocalFileName() is a file. If it is a director, this flag has no impact (it is
assumed false then).
*/
bool m_OnlyRegardOwnSeries = true;
};
class IPropertyProvider;
/** Helper function that generates a name string (e.g. for DataNode names) from the DICOM properties of the passed
provider instance. If the instance is nullptr, or has no dicom properties DataNode::NO_NAME_VALUE() will be returned.*/
std::string MITKDICOM_EXPORT GenerateNameFromDICOMProperties(const mitk::IPropertyProvider* provider);
}
#endif
diff --git a/Modules/DICOM/include/mitkDICOMSortCriterion.h b/Modules/DICOM/include/mitkDICOMSortCriterion.h
index 2a35cd3447..70aaec9949 100644
--- a/Modules/DICOM/include/mitkDICOMSortCriterion.h
+++ b/Modules/DICOM/include/mitkDICOMSortCriterion.h
@@ -1,78 +1,78 @@
/*============================================================================
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 mitkDICOMSortCriterion_h
#define mitkDICOMSortCriterion_h
#include "itkObjectFactory.h"
#include "mitkCommon.h"
#include "mitkDICOMDatasetAccess.h"
namespace mitk
{
/**
\ingroup DICOMModule
\brief A tag based sorting criterion for use in DICOMTagBasedSorter.
This class is used within std::sort (see DICOMTagBasedSorter::Sort())
and has to answer a simple question by implementing IsLeftBeforeRight().
Each time IsLeftBeforeRight() is called, the method should return whether
the left dataset should be sorted before the right dataset.
Because there are identical tags values quite often, a DICOMSortCriterion
will always hold a secondary DICOMSortCriterion. In cases of equal tag
values, the decision is referred to the secondary criterion.
*/
class MITKDICOM_EXPORT DICOMSortCriterion : public itk::LightObject
{
public:
mitkClassMacroItkParent( DICOMSortCriterion, itk::LightObject );
- /// \brief Tags used for comparison (includes seconary criteria).
+ /// \brief Tags used for comparison (includes secondary criteria).
DICOMTagList GetAllTagsOfInterest() const;
/// \brief Tags used for comparison.
virtual DICOMTagList GetTagsOfInterest() const = 0;
/// \brief Answer the sorting question.
virtual bool IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const = 0;
/// \brief Calculate a distance between two datasets.
- /// This ansers the question of consecutive datasets.
+ /// This answers the question of consecutive datasets.
virtual double NumericDistance(const mitk::DICOMDatasetAccess* from, const mitk::DICOMDatasetAccess* to) const = 0;
/// \brief The fallback criterion.
DICOMSortCriterion::ConstPointer GetSecondaryCriterion() const;
/// brief describe this class in given stream.
virtual void Print(std::ostream& os) const = 0;
virtual bool operator==(const DICOMSortCriterion& other) const = 0;
protected:
DICOMSortCriterion( DICOMSortCriterion::Pointer secondaryCriterion );
~DICOMSortCriterion() override;
bool NextLevelIsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const;
explicit DICOMSortCriterion(const DICOMSortCriterion& other);
DICOMSortCriterion& operator=(const DICOMSortCriterion& other);
DICOMSortCriterion::Pointer m_SecondaryCriterion;
};
}
#endif
diff --git a/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h b/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h
index 39a2ae15f8..26e1b0da35 100644
--- a/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h
+++ b/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h
@@ -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.
============================================================================*/
#ifndef mitkEquiDistantBlocksSorter_h
#define mitkEquiDistantBlocksSorter_h
#include "mitkDICOMDatasetSorter.h"
#include "mitkDICOMSortCriterion.h"
#include "mitkGantryTiltInformation.h"
#include "mitkVector.h"
namespace mitk
{
/**
\ingroup DICOMModule
\brief Split inputs into blocks of equidistant slices (for use in DICOMITKSeriesGDCMReader).
Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of
slices that have equal distances between neighboring slices. This is especially necessary because itk::ImageSeriesReader
is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance (see \ref DICOMITKSeriesGDCMReader_ForcedConfiguration).
To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a block.
Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to the
last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further analysis.
This grouping is done until each file has been assigned to a group.
Slices that share a position in space are also sorted into separate blocks during this step.
So the result of this step is a set of blocks that contain only slices with equal z spacing
and unique slices at each position.
During sorting, the origins (documented in tag image position patient) are compared
against expected origins (from former origin plus moving direction). As there will
be minor differences in numbers (from both calculations and imprecise tag values),
we must be a bit tolerant here. The default behavior is to expect that an origin is
not further away from the expected position than 30% of the inter-slice distance.
To support a legacy behavior of a former loader (DicomSeriesReader), this default can
be restricted to a constant number of millimeters by calling SetToleratedOriginOffset(mm).
Detailed implementation in AnalyzeFileForITKImageSeriesReaderSpacingAssumption().
*/
class MITKDICOM_EXPORT EquiDistantBlocksSorter : public DICOMDatasetSorter
{
public:
mitkClassMacro( EquiDistantBlocksSorter, DICOMDatasetSorter );
itkNewMacro( EquiDistantBlocksSorter );
DICOMTagList GetTagsOfInterest() override;
/**
\brief Delegates work to AnalyzeFileForITKImageSeriesReaderSpacingAssumption().
AnalyzeFileForITKImageSeriesReaderSpacingAssumption() is called until it does not
create multiple blocks anymore.
*/
void Sort() override;
/**
\brief Whether or not to accept images from a tilted acquisition in a single output group.
*/
void SetAcceptTilt(bool accept);
bool GetAcceptTilt() const;
/**
\brief See class description and SetToleratedOriginOffset().
*/
void SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistanct = 0.3);
/**
\brief See class description and SetToleratedOriginOffsetToAdaptive().
Default value of 0.005 is calculated so that we get a maximum of 1/10mm
error when having a measurement crosses 20 slices in z direction (too strict? we don't know better..).
*/
void SetToleratedOriginOffset(double millimeters = 0.005);
double GetToleratedOriginOffset() const;
bool IsToleratedOriginOffsetAbsolute() const;
void SetAcceptTwoSlicesGroups(bool accept);
bool GetAcceptTwoSlicesGroups() const;
void PrintConfiguration(std::ostream& os, const std::string& indent = "") const override;
bool operator==(const DICOMDatasetSorter& other) const override;
protected:
/**
\brief Return type of AnalyzeFileForITKImageSeriesReaderSpacingAssumption().
Class contains the grouping result of method AnalyzeFileForITKImageSeriesReaderSpacingAssumption(),
which takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction.
The result contains of two blocks: a first one is the grouping result, all of those images can be loaded
into one image block because they have an equal origin-to-origin distance without any gaps in-between.
*/
class SliceGroupingAnalysisResult
{
public:
SliceGroupingAnalysisResult();
/**
\brief Grouping result, all same origin-to-origin distance w/o gaps.
*/
DICOMDatasetList GetBlockDatasets();
void SetFirstFilenameOfBlock(const std::string& filename);
std::string GetFirstFilenameOfBlock() const;
void SetLastFilenameOfBlock(const std::string& filename);
std::string GetLastFilenameOfBlock() const;
/**
\brief Remaining files, which could not be grouped.
*/
DICOMDatasetList GetUnsortedDatasets();
/**
\brief Whether or not the grouped result contain a gantry tilt.
*/
bool ContainsGantryTilt();
/**
\brief Detailed description of gantry tilt.
*/
const GantryTiltInformation& GetTiltInfo() const;
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToSortedBlock(DICOMDatasetAccess* dataset);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToUnsortedBlock(DICOMDatasetAccess* dataset);
void AddFilesToUnsortedBlock(const DICOMDatasetList& datasets);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
\todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result!
*/
void FlagGantryTilt(const GantryTiltInformation& tiltInfo);
/**
\brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
*/
void UndoPrematureGrouping();
protected:
DICOMDatasetList m_GroupedFiles;
DICOMDatasetList m_UnsortedFiles;
GantryTiltInformation m_TiltInfo;
std::string m_FirstFilenameOfBlock;
std::string m_LastFilenameOfBlock;
};
/**
\brief Ensure an equal z-spacing for a group of files.
Takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction.
- Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing.
+ Internally used by GetSeries. Returns two lists: the first one contains slices of equal inter-slice spacing.
The second list contains remaining files, which need to be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption again.
Relevant code that is matched here is in
itkImageSeriesReader.txx (ImageSeriesReader<TOutputImage>::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20)
*/
SliceGroupingAnalysisResult
AnalyzeFileForITKImageSeriesReaderSpacingAssumption(const DICOMDatasetList& files, bool groupsOfSimilarImages);
/**
\brief Safely convert const char* to std::string.
*/
std::string
ConstCharStarToString(const char* s);
EquiDistantBlocksSorter();
~EquiDistantBlocksSorter() override;
EquiDistantBlocksSorter(const EquiDistantBlocksSorter& other);
EquiDistantBlocksSorter& operator=(const EquiDistantBlocksSorter& other);
bool m_AcceptTilt;
typedef std::vector<SliceGroupingAnalysisResult> ResultsList;
ResultsList m_SliceGroupingResults;
double m_ToleratedOriginOffset;
bool m_ToleratedOriginOffsetIsAbsolute;
bool m_AcceptTwoSlicesGroups;
};
}
#endif
diff --git a/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h b/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h
index c721472f2d..4bcae594c7 100644
--- a/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h
+++ b/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h
@@ -1,105 +1,105 @@
/*============================================================================
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 mitkITKDICOMSeriesReaderHelper_h
#define mitkITKDICOMSeriesReaderHelper_h
#include "mitkImage.h"
#include "mitkGantryTiltInformation.h"
#include "mitkDICOMTag.h"
#include <itkGDCMImageIO.h>
/* Forward deceleration of an DCMTK class. Used in the txx but part of the interface.*/
class OFDateTime;
namespace mitk
{
class ITKDICOMSeriesReaderHelper
{
public:
static const DICOMTag AcquisitionDateTag;
static const DICOMTag AcquisitionTimeTag;
static const DICOMTag TriggerTimeTag;
typedef std::vector<std::string> StringContainer;
typedef std::list<StringContainer> StringContainerList;
Image::Pointer Load( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo );
Image::Pointer Load3DnT( const StringContainerList& filenamesLists, bool correctTilt, const GantryTiltInformation& tiltInfo );
static bool CanHandleFile(const std::string& filename);
private:
typedef std::vector<TimeBounds> TimeBoundsList;
typedef itk::FixedArray<OFDateTime,2> DateTimeBounds;
/** Scans the given files for the acquisition time and returns the lowest and
highest acquisition date time as date time bounds via bounds.
@param bounds The acquisition date time bound extracted from the files.
@param triggerBounds Time bounds for trigger information extracted from the files.
If no trigger information was found than it returns trigger == [0.0, 0.0].
@return If no acquisition date times can be found the function return will be false. Otherwise
it returns True.
*/
static bool ExtractDateTimeBoundsAndTriggerOfTimeStep( const StringContainer& filenamesOfTimeStep,
DateTimeBounds& bounds, TimeBounds& triggerBounds);
/* Determine the time bounds in ms respective to the baselineDateTime for the passed
files. Additionally it regards the trigger time tag if set and acquisition date time
carries not enough information.*/
static bool ExtractTimeBoundsOfTimeStep(const StringContainer& filenamesOfTimeStep,
TimeBounds& bounds,
const OFDateTime& baselineDateTime );
/** Returns the list of time bounds of all passed time step containers.
(sa ExtractTimeBoundsOfTimeStep and ExtractDateTimeBoundsOfTimeStep).
Time steps where no time bounds could be extracted
- are indecated by "null" time bounds (both values "0"). The order of the returned
+ are indicated by "null" time bounds (both values "0"). The order of the returned
list equals of passed filenamesOfTimeSteps order.
@remark The function regards acquisition date time tags and trigger time tags.*/
static TimeBoundsList ExtractTimeBoundsOfTimeSteps (const StringContainerList& filenamesOfTimeSteps);
/** Helper function that generates a time geometry using the template and the passed boundslist
(which indicates the number of time steps).
*/
static TimeGeometry::Pointer GenerateTimeGeometry(const BaseGeometry* templateGeometry, const TimeBoundsList& boundsList);
template <typename ImageType>
typename ImageType::Pointer
FixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo );
template <typename PixelType>
Image::Pointer
LoadDICOMByITK( const StringContainer& filenames,
bool correctTilt,
const GantryTiltInformation& tiltInfo,
itk::GDCMImageIO::Pointer& io);
template <typename PixelType>
Image::Pointer
LoadDICOMByITK3DnT( const StringContainerList& filenames,
bool correctTilt,
const GantryTiltInformation& tiltInfo,
itk::GDCMImageIO::Pointer& io);
};
}
#endif
diff --git a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h
index 261e638e8a..f18179f78d 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h
+++ b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h
@@ -1,1033 +1,1033 @@
/*============================================================================
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 mitkDicomSeriesReader_h
#define mitkDicomSeriesReader_h
#include "mitkConfig.h"
#include "mitkDataNode.h"
#include <itkGDCMImageIO.h>
#include <itkCommand.h>
#include <itkImageSeriesReader.h>
#ifdef NOMINMAX
#define DEF_NOMINMAX
#undef NOMINMAX
#endif
#include <gdcmConfigure.h>
#ifdef DEF_NOMINMAX
#ifndef NOMINMAX
#define NOMINMAX
#endif
#undef DEF_NOMINMAX
#endif
#include <gdcmDataSet.h>
#include <gdcmScanner.h>
namespace mitk
{
/**
\brief Loading DICOM images as MITK images.
- \ref DicomSeriesReader_purpose
- \ref DicomSeriesReader_limitations
- \ref DicomSeriesReader_usage
- \ref DicomSeriesReader_sorting
- \ref DicomSeriesReader_sorting1
- \ref DicomSeriesReader_sorting2
- \ref DicomSeriesReader_sorting3
- \ref DicomSeriesReader_sorting4
- \ref DicomSeriesReader_gantrytilt
- \ref DicomSeriesReader_pixelspacing
- \ref DicomSeriesReader_nextworkitems
- \ref DicomSeriesReader_whynotinitk
- \ref DicomSeriesReader_tests
\section DicomSeriesReader_purpose Purpose
DicomSeriesReader serves as a central class for loading DICOM images as mitk::Image.
As the term "DICOM image" covers a huge variety of possible modalities and
implementations, and since MITK assumes that 3D images are made up of continuous blocks
of slices without any gaps or changes in orientation, the loading mechanism must
implement a number of decisions and compromises.
<b>The main intention of this implementation is not efficiency but correctness of generated slice positions and pixel
spacings!</b>
\section DicomSeriesReader_limitations Assumptions and limitations
The class is working only with GDCM 2.0.14 (or possibly newer). This version is the
default of an MITK super-build. Support for other versions or ITK's DicomIO was dropped
because of the associated complexity of DicomSeriesReader.
\b Assumptions
- expected to work with certain SOP Classes (mostly CT Image Storage and MR Image Storage)
- see ImageBlockDescriptor.GetReaderImplementationLevel() method for the details
- special treatment for a certain type of Philips 3D ultrasound (recognized by tag 3001,0010 set to "Philips3D")
- loader will always attempt to read multiple single slices as a single 3D image volume (i.e. mitk::Image)
- slices will be grouped by basic properties such as orientation, rows, columns, spacing and grouped into as large
blocks as possible
- images which do NOT report a position or orientation in space (Image Position Patient, Image Orientation) will be
assigned defaults
- image position (0,0,0)
- image orientation (1,0,0), (0,1,0)
- such images will always be grouped separately since spatial grouping / sorting makes no sense for them
\b Options
- images that cover the same piece of space (i.e. position, orientation, and dimensions are equal)
can be interpreted as time-steps of the same image, i.e. a series will be loaded as 3D+t
\b Limitations
- the 3D+t assumption only works if all time-steps have an equal number of slices and if all
have the Acquisition Time attribute set to meaningful values
\section DicomSeriesReader_usage Usage
The starting point for an application is a set of DICOM files that should be loaded.
For convenience, DicomSeriesReader can also parse a whole directory for DICOM files,
but an application should better know exactly what to load.
Loading is then done in two steps:
1. <b>Group the files into spatial blocks</b> by calling GetSeries().
This method will sort all passed files into meaningful blocks that
could fit into an mitk::Image. Sorting for 3D+t loading is optional but default.
The \b return value of this function is a list of descriptors, which
describe a grouped list of files with its most basic properties:
- SOP Class (CT Image Storage, Secondary Capture Image Storage, etc.)
- Modality
- What type of pixel spacing can be read from the provided DICOM tags
- How well DicomSeriesReader is prepared to load this type of data
2. <b>Load a sorted set of files</b> by calling LoadDicomSeries().
This method expects go receive the sorting output of GetSeries().
The method will then invoke ITK methods configured with GDCM-IO
classes to actually load the files into memory and put them into
mitk::Images. Again, loading as 3D+t is optional.
Example:
\code
// only a directory is known at this point: /home/who/dicom
DicomSeriesReader::FileNamesGrouping allImageBlocks = DicomSeriesReader::GetSeries("/home/who/dicom/");
// file now divided into groups of identical image size, orientation, spacing, etc.
// each of these lists should be loadable as an mitk::Image.
DicomSeriesReader::StringContainer seriesToLoad = allImageBlocks[...]; // decide what to load
// final step: load into DataNode (can result in 3D+t image)
DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( oneBlockSorted );
itk::SmartPointer<Image> image = dynamic_cast<mitk::Image*>( node->GetData() );
\endcode
\section DicomSeriesReader_sorting Logic for sorting 2D slices from DICOM images into 3D+t blocks for mitk::Image
The general sorting mechanism (implemented in GetSeries) groups and sorts a set of DICOM files, each assumed to
contain
a single CT/MR slice.
In the following we refer to those file groups as "blocks", since this is what they are meant to become when loaded
into an mitk::Image.
\subsection DicomSeriesReader_sorting1 Step 1: Avoiding pure non-sense
A first pass separates slices that cannot possibly be loaded together because of restrictions of mitk::Image.
After this steps, each block contains only slices that match in all of the following DICOM tags:
- (0020,000e) Series Instance UID
- (0020,0037) Image Orientation
- (0028,0030) Pixel Spacing
- (0018,1164) Imager Pixel Spacing
- (0018,0050) Slice Thickness
- (0028,0010) Number Of Rows
- (0028,0011) Number Of Columns
- (0028,0008) Number Of Frames
\subsection DicomSeriesReader_sorting2 Step 2: Sort slices spatially
Before slices are further analyzed, they are sorted spatially. As implemented by GdcmSortFunction(),
slices are sorted by
1. distance from origin (calculated using (0020,0032) Image Position Patient and (0020,0037) Image Orientation)
2. when distance is equal, (0020,0012) Acquisition Number, (0008,0032) Acquisition Time and (0018,1060) Trigger Time
are
used as a backup criterions (necessary for meaningful 3D+t sorting)
\subsection DicomSeriesReader_sorting3 Step 3: Ensure equal z spacing
Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of
slices that have equal distances between neighboring slices. This is especially necessary because
itk::ImageSeriesReader
is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance (see
\ref DicomSeriesReader_whatweknowaboutitk).
To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a
block.
Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to
the
last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further
analysis.
This grouping is done until each file has been assigned to a group.
Slices that share a position in space are also sorted into separate blocks during this step.
So the result of this step is a set of blocks that contain only slices with equal z spacing
and unique slices at each position.
\subsection DicomSeriesReader_sorting4 Step 4 (optional): group 3D blocks as 3D+t when possible
This last step depends on an option of GetSeries(). When requested, image blocks from the previous step are merged
again
whenever two blocks occupy the same portion of space (i.e. same origin, number of slices and z-spacing).
\section DicomSeriesReader_gantrytilt Handling of gantry tilt
When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align
anymore. This scanner feature is used for example to reduce metal artifacts (e.g. <i>Lee C , Evaluation of Using CT
Gantry Tilt Scan on Head and Neck Cancer Patients with Dental Structure: Scans Show Less Metal Artifacts. Presented
at: Radiological Society of North America 2011 Scientific Assembly and Annual Meeting; November 27- December 2,
2011 Chicago IL.</i>).
The acquired planes of such CT series do not match the expectations of a orthogonal geometry in mitk::Image: if you
stack the slices, they show a small shift along the Y axis:
\verbatim
without tilt with tilt
|||||| //////
|||||| //////
-- |||||| --------- ////// -------- table orientation
|||||| //////
|||||| //////
Stacked slices:
without tilt with tilt
-------------- --------------
-------------- --------------
-------------- --------------
-------------- --------------
-------------- --------------
\endverbatim
As such gemetries do not in conjunction with mitk::Image, DicomSeriesReader performs a correction for such series
if the groupImagesWithGantryTilt or correctGantryTilt flag in GetSeries and LoadDicomSeries is set (default = on).
The correction algorithms undoes two errors introduced by ITK's ImageSeriesReader:
- the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using
itk::ResampleFilter.
- the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the
slice distance in this special case)
Both errors are introduced in
itkImageSeriesReader.txx (ImageSeriesReader<TOutputImage>::GenerateOutputInformation(void)), lines 176 to 245 (as of
ITK 3.20)
For the correction, we examine two consecutive slices of a series, both described as a pair (origin/orientation):
- we calculate if the first origin is on a line along the normal of the second slice
- if this is not the case, the geometry will not fit a normal mitk::Image/mitk::Geometry3D
- we then project the second origin into the first slice's coordinate system to quantify the shift
- both is done in class GantryTiltInformation with quite some comments.
The geometry of image stacks with tilted geometries is illustrated below:
- green: the DICOM images as described by their tags: origin as a point with the line indicating the orientation
- red: the output of ITK ImageSeriesReader: wrong, larger spacing, no tilt
- blue: how much a shear must correct
\image html Modules/DICOM/doc/Doxygen/tilt-correction.jpg
\section DicomSeriesReader_whatweknowaboutitk The actual image loading process
When calling LoadDicomSeries(), this method "mainly" uses an instance of itk::ImageSeriesReader,
configured with an itk::GDCMImageIO object. Because DicomSeriesReader works around some of the
behaviors of these classes, the following is a list of features that we find in the code and need to work with:
- itk::ImageSeriesReader::GenerateOutputInformation() does the z-spacing handling
+ spacing is directly determined by comparing (euclidean distance) the origins of the first two slices of a series
* this is GOOD because there is no reliable z-spacing information in DICOM images
* this is bad because it does not work with gantry tilt, in which case the slice distance is SMALLER than the
distance between two origins (see section on tilt)
- origin and spacing are calculated by GDCMImageIO and re-used in itk::ImageSeriesReader
+ the origins are read from appropriate tags, nothing special about that
+ the spacing is read by gdcm::ImageReader, gdcm::ImageHelper::GetSpacingValue() from a tag determined by
gdcm::ImageHelper::GetSpacingTagFromMediaStorage(), which basically determines ONE appropriate pixel spacing tag for
each media storage type (ct image, mr image, secondary capture image, etc.)
* this is fine for modalities such as CT/MR where the "Pixel Spacing" tag is mandatory, but for other modalities
such as CR or Secondary Capture, the tag "Imager Pixel Spacing" is taken, which is no only optional but also has a
more
complicated relation with the "Pixel Spacing" tag. For this reason we check/modify the pixel spacing reported by
itk::ImageSeriesReader after loading the image (see \ref DicomSeriesReader_pixelspacing)
AFTER loading, DicomSeriesReader marks some of its findings as mitk::Properties to the loaded Image and DataNode:
- <b>dicomseriesreader.SOPClass</b> : DICOM SOP Class as readable string (instead of a UID)
- <b>dicomseriesreader.ReaderImplementationLevelString</b> : Confidence /Support level of the reader for this image
as
readable string
- <b>dicomseriesreader.ReaderImplementationLevel</b> : Confidence /Support level of the reader for this image as
enum
value of type ReaderImplementationLevel
- <b>dicomseriesreader.PixelSpacingInterpretationString</b> : Appropriate interpreteation of pixel spacing for this
Image as readable string
- <b>dicomseriesreader.PixelSpacingInterpretation</b> : Appropriate interpreteation of pixel spacing for this Image
as
enum value of type PixelSpacingInterpretation
- <b>dicomseriesreader.MultiFrameImage</b> : bool flag to mark multi-frame images
- <b>dicomseriesreader.GantyTiltCorrected</b> : bool flag to mark images where a gantry tilt was corrected to fit
slices into an mitk::Image
- <b>dicomseriesreader.3D+t</b> : bool flag to mark images with a time dimension (multiple 3D blocks of the same
size
at the same position in space)
\section DicomSeriesReader_pixelspacing Handling of pixel spacing
The reader implements what is described in DICOM Part 3, chapter 10.7 (Basic Pixel Spacing Calibration Macro): Both
tags
- (0028,0030) Pixel Spacing and
- (0018,1164) Imager Pixel Spacing
are evaluated and the pixel spacing is set to the spacing within the patient when tags allow that.
The result of pixel spacing interpretation can be read from a property
"dicomseriesreader.PixelSpacingInterpretation",
which refers to one of the enumerated values of type PixelSpacingInterpretation;
\section DicomSeriesReader_supportedmodalities Limitations for specific modalities
- <b>Enhanced Computed Tomography / Magnetic Resonance Images</b> are currently NOT supported at all, because we
lack
general support for multi-frame images.
- <b>Nuclear Medicine Images</b> are not supported fully supported, only the single-frame variants are loaded
properly.
\section DicomSeriesReader_nextworkitems Possible enhancements
This is a short list of ideas for enhancement:
- Class has historically grown and should be reviewed again. There is probably too many duplicated scanning code
- Multi-frame images don't mix well with the current assumption of "one file - one slice", which is assumed by our
code
- It should be checked how well GDCM and ITK support these files (some load, some don't)
- Specializations such as the Philips 3D code should be handled in a more generic way. The current handling of
Philips 3D images is not nice at all
\section DicomSeriesReader_whynotinitk Why is this not in ITK?
Some of this code would probably be better located in ITK. It is just a matter of resources that this is not the
case yet. Any attempts into this direction are welcome and can be supported. At least the gantry tilt correction
should be a simple addition to itk::ImageSeriesReader.
\section DicomSeriesReader_tests Tests regarding DICOM loading
A number of tests have been implemented to check our assumptions regarding DICOM loading. Please see \ref
DICOMTesting
\todo refactor all the protected helper objects/methods into a separate header so we compile faster
*/
class Image;
class DicomSeriesReader
{
public:
/**
\brief Lists of filenames.
*/
typedef std::vector<std::string> StringContainer;
/**
\brief Interface for the progress callback.
*/
typedef void (*UpdateCallBackMethod)(float);
/**
\brief Describes how well the reader is tested for a certain file type.
Applications should not rely on the outcome for images which are reported
ReaderImplementationLevel_Implemented or ReaderImplementationLevel_Unsupported.
Errors to load images which are reported as ReaderImplementationLevel_Supported
are considered bugs. For ReaderImplementationLevel_PartlySupported please check the appropriate paragraph in \ref
DicomSeriesReader_supportedmodalities
*/
typedef enum {
ReaderImplementationLevel_Supported, /// loader code and tests are established
ReaderImplementationLevel_PartlySupported, /// loader code and tests are established for specific parts of a SOP
/// Class
ReaderImplementationLevel_Implemented, /// loader code is implemented but not accompanied by tests
ReaderImplementationLevel_Unsupported, /// loader code is not working with this SOP Class
} ReaderImplementationLevel;
/**
\brief How the mitk::Image spacing should be interpreted.
Compare DICOM PS 3.3 10.7 (Basic Pixel Spacing Calibration Macro).
*/
typedef enum {
PixelSpacingInterpretation_SpacingInPatient, /// distances are mm within a patient
PixelSpacingInterpretation_SpacingAtDetector, /// distances are mm at detector surface
PixelSpacingInterpretation_SpacingUnknown /// NO spacing information is present, we use (1,1) as default
} PixelSpacingInterpretation;
/**
\brief Return type of GetSeries, describes a logical group of files.
Files grouped into a single 3D or 3D+t block are described by an instance
of this class. Relevant descriptive properties can be used to provide
the application user with meaningful choices.
*/
class ImageBlockDescriptor
{
public:
/// List of files in this group
StringContainer GetFilenames() const;
/// A unique ID describing this bloc (enhanced Series Instance UID).
std::string GetImageBlockUID() const;
/// The Series Instance UID.
std::string GetSeriesInstanceUID() const;
/// Series Modality (CT, MR, etc.)
std::string GetModality() const;
/// SOP Class UID as readable string (Computed Tomography Image Storage, Secondary Capture Image Storage, etc.)
std::string GetSOPClassUIDAsString() const;
/// SOP Class UID as DICOM UID
std::string GetSOPClassUID() const;
/// Confidence of the reader that this block can be read successfully.
ReaderImplementationLevel GetReaderImplementationLevel() const;
/// Whether or not the block contains a gantry tilt which will be "corrected" during loading
bool HasGantryTiltCorrected() const;
/// Whether or not mitk::Image spacing relates to the patient
bool PixelSpacingRelatesToPatient() const;
/// Whether or not mitk::Image spacing relates to the detector surface
bool PixelSpacingRelatesToDetector() const;
/// Whether or not mitk::Image spacing is of unknown origin
bool PixelSpacingIsUnknown() const;
/// How the mitk::Image spacing can meaningfully be interpreted.
PixelSpacingInterpretation GetPixelSpacingType() const;
/// 3D+t or not
bool HasMultipleTimePoints() const;
/// Multi-frame image(s) or not
bool IsMultiFrameImage() const;
ImageBlockDescriptor();
~ImageBlockDescriptor();
private:
friend class DicomSeriesReader;
ImageBlockDescriptor(const StringContainer &files);
void AddFile(const std::string &file);
void AddFiles(const StringContainer &files);
void SetImageBlockUID(const std::string &uid);
void SetSeriesInstanceUID(const std::string &uid);
void SetModality(const std::string &modality);
void SetNumberOfFrames(const std::string &);
void SetSOPClassUID(const std::string &mediaStorageSOPClassUID);
void SetHasGantryTiltCorrected(bool);
void SetPixelSpacingInformation(const std::string &pixelSpacing, const std::string &imagerPixelSpacing);
void SetHasMultipleTimePoints(bool);
void GetDesiredMITKImagePixelSpacing(ScalarType &spacingX, ScalarType &spacingY) const;
StringContainer m_Filenames;
std::string m_ImageBlockUID;
std::string m_SeriesInstanceUID;
std::string m_Modality;
std::string m_SOPClassUID;
bool m_HasGantryTiltCorrected;
std::string m_PixelSpacing;
std::string m_ImagerPixelSpacing;
bool m_HasMultipleTimePoints;
bool m_IsMultiFrameImage;
};
typedef std::map<std::string, ImageBlockDescriptor> FileNamesGrouping;
/**
\brief Provide combination of preprocessor defines that was active during compilation.
Since this class is a combination of several possible implementations, separated only
by ifdef's, calling instances might want to know which flags were active at compile time.
*/
static std::string GetConfigurationString();
/**
\brief Checks if a specific file contains DICOM data.
*/
static bool IsDicom(const std::string &filename);
/**
\brief see other GetSeries().
Find all series (and sub-series -- see details) in a particular directory.
*/
static FileNamesGrouping GetSeries(const std::string &dir,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief see other GetSeries().
\warning Untested, could or could not work.
This differs only by having an additional restriction to a single known DICOM series.
Internally, it uses the other GetSeries() method.
*/
static StringContainer GetSeries(const std::string &dir,
const std::string &series_uid,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief PREFERRED version of this method - scan and sort DICOM files.
Parse a list of files for images of DICOM series.
For each series, an enumeration of the files contained in it is created.
\return The resulting maps UID-like keys (based on Series Instance UID and slice properties) to sorted lists of
file
names.
SeriesInstanceUID will be enhanced to be unique for each set of file names
that is later loadable as a single mitk::Image. This implies that
Image orientation, slice thickness, pixel spacing, rows, and columns
must be the same for each file (i.e. the image slice contained in the file).
If this separation logic requires that a SeriesInstanceUID must be made more specialized,
it will follow the same logic as itk::GDCMSeriesFileNames to enhance the UID with
more digits and dots.
Optionally, more tags can be used to separate files into different logical series by setting
the restrictions parameter.
\warning Adding restrictions is not yet implemented!
*/
static FileNamesGrouping GetSeries(const StringContainer &files,
bool sortTo3DPlust,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief See other GetSeries().
Use GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions) instead.
*/
static FileNamesGrouping GetSeries(const StringContainer &files,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
Loads a DICOM series composed by the file names enumerated in the file names container.
If a callback method is supplied, it will be called after every progress update with a progress value in [0,1].
\param filenames The filenames to load.
\param sort Whether files should be sorted spatially (true) or not (false - maybe useful if presorted)
\param load4D Whether to load the files as 3D+t (if possible)
\param correctGantryTilt
\param callback
\param preLoadedImageBlock
*/
static DataNode::Pointer LoadDicomSeries(const StringContainer &filenames,
bool sort = true,
bool load4D = true,
bool correctGantryTilt = true,
UpdateCallBackMethod callback = nullptr,
itk::SmartPointer<Image> preLoadedImageBlock = nullptr);
/**
\brief See LoadDicomSeries! Just a slightly different interface.
If \p preLoadedImageBlock is provided, the reader will only "fake" loading and create appropriate mitk::Properties.
\param filenames
\param node
\param sort
\param load4D
\param correctGantryTilt
\param callback
\param preLoadedImageBlock
*/
static bool LoadDicomSeries(const StringContainer &filenames,
DataNode &node,
bool sort = true,
bool load4D = true,
bool correctGantryTilt = true,
UpdateCallBackMethod callback = nullptr,
itk::SmartPointer<Image> preLoadedImageBlock = nullptr);
protected:
/**
\brief Return type of DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
Class contains the grouping result of method
DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption,
which takes as input a number of images, which are all equally oriented and spatially sorted along their normal
direction.
The result contains of two blocks: a first one is the grouping result, all of those images can be loaded
into one image block because they have an equal origin-to-origin distance without any gaps in-between.
*/
class SliceGroupingAnalysisResult
{
public:
SliceGroupingAnalysisResult();
/**
\brief Grouping result, all same origin-to-origin distance w/o gaps.
*/
StringContainer GetBlockFilenames();
/**
\brief Remaining files, which could not be grouped.
*/
StringContainer GetUnsortedFilenames();
/**
\brief Whether or not the grouped result contain a gantry tilt.
*/
bool ContainsGantryTilt();
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToSortedBlock(const std::string &filename);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToUnsortedBlock(const std::string &filename);
void AddFilesToUnsortedBlock(const StringContainer &filenames);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
\todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result!
*/
void FlagGantryTilt();
/**
\brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
*/
void UndoPrematureGrouping();
protected:
StringContainer m_GroupedFiles;
StringContainer m_UnsortedFiles;
bool m_GantryTilt;
};
/**
\brief Gantry tilt analysis result.
Takes geometry information for two slices of a DICOM series and
calculates whether these fit into an orthogonal block or not.
If NOT, they can either be the result of an acquisition with
gantry tilt OR completely broken by some shearing transformation.
Most calculations are done in the constructor, results can then
be read via the remaining methods.
*/
class GantryTiltInformation
{
public:
// two types to avoid any rounding errors
typedef itk::Point<double, 3> Point3Dd;
typedef itk::Vector<double, 3> Vector3Dd;
/**
\brief Just so we can create empty instances for assigning results later.
*/
GantryTiltInformation();
/**
\brief THE constructor, which does all the calculations.
Determining the amount of tilt is done by checking the distances
of origin1 from planes through origin2. Two planes are considered:
- normal vector along normal of slices (right x up): gives the slice distance
- normal vector along orientation vector "up": gives the shift parallel to the plane orientation
The tilt angle can then be calculated from these distances
\param origin1 origin of the first slice
\param origin2 origin of the second slice
\param right right/up describe the orientatation of borth slices
\param up right/up describe the orientatation of borth slices
\param numberOfSlicesApart how many slices are the given origins apart (1 for neighboring slices)
*/
GantryTiltInformation(const Point3D &origin1,
const Point3D &origin2,
const Vector3D &right,
const Vector3D &up,
unsigned int numberOfSlicesApart);
/**
\brief Whether the slices were sheared.
True if any of the shifts along right or up vector are non-zero.
*/
bool IsSheared() const;
/**
\brief Whether the shearing is a gantry tilt or more complicated.
Gantry tilt will only produce shifts in ONE orientation, not in both.
Since the correction code currently only covers one tilt direction
AND we don't know of medical images with two tilt directions, the
loading code wants to check if our assumptions are true.
*/
bool IsRegularGantryTilt() const;
/**
\brief The offset distance in Y direction for each slice in mm (describes the tilt result).
*/
double GetMatrixCoefficientForCorrectionInWorldCoordinates() const;
/**
\brief The z / inter-slice spacing. Needed to correct ImageSeriesReader's result.
*/
double GetRealZSpacing() const;
/**
\brief The shift between first and last slice in mm.
Needed to resize an orthogonal image volume.
*/
double GetTiltCorrectedAdditionalSize() const;
/**
\brief Calculated tilt angle in degrees.
*/
double GetTiltAngleInDegrees() const;
protected:
/**
\brief Projection of point p onto line through lineOrigin in direction of lineDirection.
*/
Point3D projectPointOnLine(Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection);
double m_ShiftUp;
double m_ShiftRight;
double m_ShiftNormal;
double m_ITKAssumedSliceSpacing;
unsigned int m_NumberOfSlicesApart;
};
/**
\brief for internal sorting.
*/
typedef std::pair<StringContainer, StringContainer> TwoStringContainers;
/**
\brief Maps DICOM tags to MITK properties.
*/
typedef std::map<std::string, std::string> TagToPropertyMapType;
/**
\brief Ensure an equal z-spacing for a group of files.
Takes as input a number of images, which are all equally oriented and spatially sorted along their normal
direction.
- Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing.
+ Internally used by GetSeries. Returns two lists: the first one contains slices of equal inter-slice spacing.
The second list contains remaining files, which need to be run through
AnalyzeFileForITKImageSeriesReaderSpacingAssumption again.
Relevant code that is matched here is in
itkImageSeriesReader.txx (ImageSeriesReader<TOutputImage>::GenerateOutputInformation(void)), lines 176 to 245 (as
of
ITK 3.20)
*/
static SliceGroupingAnalysisResult AnalyzeFileForITKImageSeriesReaderSpacingAssumption(
const StringContainer &files, bool groupsOfSimilarImages, const gdcm::Scanner::MappingType &tagValueMappings_);
/**
\brief Safely convert const char* to std::string.
*/
static std::string ConstCharStarToString(const char *s);
/**
\brief Safely convert a string into pixel spacing x and y.
*/
static bool DICOMStringToSpacing(const std::string &s, ScalarType &spacingX, ScalarType &spacingY);
/**
\brief Convert DICOM string describing a point to Point3D.
DICOM tags like ImagePositionPatient contain a position as float numbers separated by backslashes:
\verbatim
42.7131\13.77\0.7
\endverbatim
*/
static Point3D DICOMStringToPoint3D(const std::string &s, bool &successful);
/**
\brief Convert DICOM string describing a point two Vector3D.
DICOM tags like ImageOrientationPatient contain two vectors as float numbers separated by backslashes:
\verbatim
42.7131\13.77\0.7\137.76\0.3
\endverbatim
*/
static void DICOMStringToOrientationVectors(const std::string &s, Vector3D &right, Vector3D &up, bool &successful);
template <typename ImageType>
static typename ImageType::Pointer
// TODO this is NOT inplace!
InPlaceFixUpTiltedGeometry(ImageType *input, const GantryTiltInformation &tiltInfo);
/**
\brief Sort a set of file names in an order that is meaningful for loading them into an mitk::Image.
\warning This method assumes that input files are similar in basic properties such as
slice thickness, image orientation, pixel spacing, rows, columns.
It should always be ok to put the result of a call to GetSeries(..) into this method.
Sorting order is determined by
1. image position along its normal (distance from world origin)
2. acquisition time
If P<n> denotes a position and T<n> denotes a time step, this method will order slices from three timesteps like
this:
\verbatim
P1T1 P1T2 P1T3 P2T1 P2T2 P2T3 P3T1 P3T2 P3T3
\endverbatim
*/
static StringContainer SortSeriesSlices(const StringContainer &unsortedFilenames);
public:
/**
\brief Checks if a specific file is a Philips3D ultrasound DICOM file.
*/
static bool IsPhilips3DDicom(const std::string &filename);
static std::string ReaderImplementationLevelToString(const ReaderImplementationLevel &enumValue);
static std::string PixelSpacingInterpretationToString(const PixelSpacingInterpretation &enumValue);
protected:
/**
\brief Read a Philips3D ultrasound DICOM file and put into an mitk::Image.
*/
static bool ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer<Image> output_image);
/**
\brief Construct a UID that takes into account sorting criteria from GetSeries().
*/
static std::string CreateMoreUniqueSeriesIdentifier(gdcm::Scanner::TagToValue &tagValueMap);
/**
\brief Helper for CreateMoreUniqueSeriesIdentifier
*/
static std::string CreateSeriesIdentifierPart(gdcm::Scanner::TagToValue &tagValueMap, const gdcm::Tag &tag);
/**
\brief Helper for CreateMoreUniqueSeriesIdentifier
*/
static std::string IDifyTagValue(const std::string &value);
typedef itk::GDCMImageIO DcmIoType;
/**
\brief Progress callback for DicomSeriesReader.
*/
class CallbackCommand : public itk::Command
{
public:
CallbackCommand(UpdateCallBackMethod callback) : m_Callback(callback) {}
void Execute(const itk::Object *caller, const itk::EventObject &) override
{
(*this->m_Callback)(static_cast<const itk::ProcessObject *>(caller)->GetProgress());
}
void Execute(itk::Object *caller, const itk::EventObject &) override
{
(*this->m_Callback)(static_cast<itk::ProcessObject *>(caller)->GetProgress());
}
protected:
UpdateCallBackMethod m_Callback;
};
static void FixSpacingInformation(Image *image, const ImageBlockDescriptor &imageBlockDescriptor);
/**
\brief Scan for slice image information
*/
static void ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner &scanner);
/**
\brief Performs actual loading of a series and creates an image having the specified pixel type.
*/
static void LoadDicom(const StringContainer &filenames,
DataNode &node,
bool sort,
bool check_4d,
bool correctTilt,
UpdateCallBackMethod callback,
itk::SmartPointer<Image> preLoadedImageBlock);
/**
\brief Feed files into itk::ImageSeriesReader and retrieve a 3D MITK image.
\param correctTilt
\param tiltInfo
\param io
\param command can be used for progress reporting
\param preLoadedImageBlock
*/
template <typename PixelType>
static itk::SmartPointer<Image> LoadDICOMByITK(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITKScalar(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITKRGBPixel(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
template <typename PixelType>
static itk::SmartPointer<Image> LoadDICOMByITK4D(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK4D(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK4DScalar(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK4DRGBPixel(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
/**
\brief Sort files into time step blocks of a 3D+t image.
Called by LoadDicom. Expects to be fed a single list of filenames that have been sorted by
GetSeries previously (one map entry). This method will check how many timestep can be filled
with given files.
Assumption is that the number of time steps is determined by how often the first position in
space repeats. I.e. if the first three files in the input parameter all describe the same
location in space, we'll construct three lists of files. and sort the remaining files into them.
\todo We can probably remove this method if we somehow transfer 3D+t information from GetSeries to
LoadDicomSeries.
*/
static std::list<StringContainer> SortIntoBlocksFor3DplusT(const StringContainer &presortedFilenames,
const gdcm::Scanner::MappingType &tagValueMappings_,
bool sort,
bool &canLoadAs4D);
/**
\brief Defines spatial sorting for sorting by GDCM 2.
Sorts by image position along image normal (distance from world origin).
In cases of conflict, acquisition time is used as a secondary sort criterium.
*/
static bool GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2);
/**
\brief Copy information about files and DICOM tags from ITK's MetaDataDictionary
and from the list of input files to the PropertyList of mitk::Image.
\todo Tag copy must follow; image level will cause some additional files parsing, probably.
*/
static void CopyMetaDataToImageProperties(StringContainer filenames,
const gdcm::Scanner::MappingType &tagValueMappings_,
DcmIoType *io,
const ImageBlockDescriptor &blockInfo,
Image *image);
static void CopyMetaDataToImageProperties(std::list<StringContainer> imageBlock,
const gdcm::Scanner::MappingType &tagValueMappings_,
DcmIoType *io,
const ImageBlockDescriptor &blockInfo,
Image *image);
/**
\brief Map between DICOM tags and MITK properties.
Uses as a positive list for copying specified DICOM tags (from ITK's ImageIO)
to MITK properties. ITK provides MetaDataDictionary entries of form
"gggg|eeee" (g = group, e = element), e.g. "0028,0109" (Largest Pixel in Series),
which we want to sort as dicom.series.largest_pixel_in_series".
*/
static const TagToPropertyMapType &GetDICOMTagsToMITKPropertyMap();
};
}
#endif
diff --git a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h
index e39652cd83..12992a1fdc 100644
--- a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h
+++ b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h
@@ -1,69 +1,69 @@
/*============================================================================
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 mitkDICOMPMIO_h
#define mitkDICOMPMIO_h
#include <mitkAbstractFileIO.h>
#include <mitkImage.h>
#include <dcmtk/dcmpmap/dpmparametricmapiod.h>
#include <dcmqi/JSONSegmentationMetaInformationHandler.h>
#include <dcmqi/JSONParametricMapMetaInformationHandler.h>
namespace mitk
{
/**
* Read and Writes a Parametric map to a dcm file
* @ingroup Process
*/
class DICOMPMIO : public mitk::AbstractFileIO
{
public:
DICOMPMIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
protected:
/**
* @brief Reads a DICOM parametric map from the file system
* @return an mitk::Image
- * @throws an mitk::Exception if an error ocurrs
+ * @throws an mitk::Exception if an error occurs
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
private:
typedef mitk::Image PMInputType;
typedef itk::Image<double, 3> PMitkInputImageType;
typedef IODFloatingPointImagePixelModule::value_type PMFloatPixelType; // input type required for DCMQI
typedef itk::Image<PMFloatPixelType, 3> PMitkInternalImageType;
DICOMPMIO *IOClone() const override;
// -------------- DICOMPMIO specific functions -------------
const std::string CreateMetaDataJsonFilePM() const;
};
} // end of namespace mitk
#endif
diff --git a/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp b/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp
index a96055eaef..11b266f752 100644
--- a/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp
+++ b/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp
@@ -1,132 +1,132 @@
/*============================================================================
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.
============================================================================*/
/*
This test is meant to reproduce the following error:
- The machine or current user has a German locale.
- This esp. means that stream IO expects the decimal separator as a comma: ","
- DICOM files use a point "." as the decimal separator to be locale independent
- The parser used by MITK (ITK's GDCM) seems to use the current locale instead of the "C" or "POSIX" locale
- This leads to spacings (and probably other numbers) being trimmed/rounded,
e.g. the correct spacing of 0.314 is read as 1.0 etc.
*/
#include "mitkStandardFileLocations.h"
#include "mitkTestDICOMLoading.h"
#include "mitkTestingMacros.h"
#include <list>
#include <locale>
#include <locale.h>
bool mitkDICOMLocaleTestChangeLocale(const std::string& locale)
{
try
{
MITK_TEST_OUTPUT(<< " ** Changing locale from " << setlocale(LC_ALL, nullptr) << " to '" << locale << "'");
setlocale(LC_ALL, locale.c_str());
std::locale l( locale.c_str() );
std::cin.imbue(l);
return true;
}
catch(...)
{
MITK_TEST_OUTPUT(<< "Could not activate locale " << locale);
return false;
}
}
void mitkDICOMLocaleTestWithReferenceImage(std::string filename)
{
mitk::TestDICOMLoading loader;
mitk::TestDICOMLoading::ImageList images = loader.LoadFiles({ filename });
MITK_TEST_CONDITION_REQUIRED(images.size() > 0, "file " << filename << " loaded");
mitk::DataNode::Pointer node = factory->GetOutput( 0 );
image = dynamic_cast<mitk::Image*>(node->GetData());
mitk::Image::Pointer image;
if(images.empty())
{
MITK_TEST_FAILED_MSG(<< "File "<< filename << " is not an image - test will not be applied." );
return;
}
else
{
image = images[0];
}
// note importance of minor differences in spacings:
// DICOM has order y-spacing, x-spacing, while in MITK we assume x-spacing, y-spacing (both meant for 0 and 1 index in array)
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(image->GetGeometry()->GetSpacing()[0], 0.3141592), "correct x spacing? found "
<< image->GetGeometry()->GetSpacing()[0]);
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(image->GetGeometry()->GetSpacing()[1], 0.3411592), "correct y spacing? found "
<< image->GetGeometry()->GetSpacing()[1]);
}
int mitkDICOMLocaleTest(int argc, char* argv[])
{
MITK_TEST_BEGIN("DICOMLocaleTest");
MITK_TEST_CONDITION_REQUIRED(argc >= 2, "File to load has been specified on commandline");
MITK_TEST_OUTPUT(<< "Configuration: \n" << mitk::DicomSeriesReader::GetConfigurationString() );
std::string filename = argv[1];
// load a reference DICOM file with the "C" locale being set
mitkDICOMLocaleTestChangeLocale("C");
mitkDICOMLocaleTestWithReferenceImage(filename);
// load a reference DICOM file with German locales being set
typedef std::list<std::string> StringList;
StringList alllocales;
alllocales.push_back("de_DE");
alllocales.push_back("de_DE.utf8");
alllocales.push_back("de_DE.UTF8");
alllocales.push_back("de_DE@euro");
alllocales.push_back("German_Germany");
- // supressing this test to be run on MacOS X
+ // suppressing this test to be run on MacOS X
// See bug #3894
#if defined (__APPLE__) || defined(MACOSX)
alllocales.push_back("C");
#endif
unsigned int numberOfTestedGermanLocales(0);
for (StringList::iterator iter = alllocales.begin();
iter != alllocales.end();
++iter)
{
if ( mitkDICOMLocaleTestChangeLocale(*iter) )
{
++numberOfTestedGermanLocales;
mitkDICOMLocaleTestWithReferenceImage(filename);
}
}
if(numberOfTestedGermanLocales == 0)
{
MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system.");
}
//MITK_TEST_CONDITION_REQUIRED( numberOfTestedGermanLocales > 0, "Verify that at least one German locale has been tested.");
MITK_TEST_END();
}
diff --git a/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h b/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h
index c6b3dd0644..4ced1979d2 100644
--- a/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h
+++ b/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h
@@ -1,113 +1,113 @@
/*============================================================================
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 QmitkDicomLocalStorageWidget_h
#define QmitkDicomLocalStorageWidget_h
#include "ui_QmitkDicomLocalStorageWidgetControls.h"
#include <MitkDICOMUIExports.h>
// include QT
#include <QHash>
#include <QString>
#include <QStringList>
#include <QVariant>
#include <QWidget>
class QProgressDialog;
class QLabel;
class ctkDICOMDatabase;
class ctkDICOMIndexer;
/**
* \brief QmitkDicomLocalStorageWidget is a QWidget providing functionality for dicom storage and import.
*
* \ingroup Functionalities
*/
class MITKDICOMUI_EXPORT QmitkDicomLocalStorageWidget : public QWidget
{
// this is needed for all Qt objects that should have a Qt meta-object
// (everything that derives from QObject and wants to have signal/slots)
Q_OBJECT
public:
static const std::string Widget_ID;
/**
* \brief QmitkDicomLocalStorageWidget(QWidget *parent) constructor.
*
* \param parent is a pointer to the parent widget
*/
QmitkDicomLocalStorageWidget(QWidget *parent);
/**
* \brief QmitkDicomExternalDataWidget destructor.
*/
~QmitkDicomLocalStorageWidget() override;
/**
* \brief CreateQtPartControl(QWidget *parent) sets the view objects from ui_QmitkDicomExternalDataWidgetControls.h.
*
* \param parent is a pointer to the parent widget
*/
virtual void CreateQtPartControl(QWidget *parent);
/**
* \brief SetDatabaseDirectory sets database directory.
*
- * \param newDatabaseDirectory contains path to new database directoy.
+ * \param newDatabaseDirectory contains path to new database directory.
*/
void SetDatabaseDirectory(QString newDatabaseDirectory);
signals:
/// @brief emitted when import into database is finished.
void SignalFinishedImport();
/**
* @brief emitted when view button is clicked.
* @param _t1 containing dicom UIDs properties.
*/
void SignalDicomToDataManager(QHash<QString, QVariant> _t1);
/// \brief emitted if cancel button is pressed.
void SignalCancelImport();
public slots:
/// @brief Called when view button was clicked.
void OnViewButtonClicked();
/// @brief Called delete button was clicked.
void OnDeleteButtonClicked();
/// @brief Called when adding a dicom directory. Starts a thread adding the directory.
void OnStartDicomImport(const QString &dicomData);
/// @brief Called when adding a list of dicom files. Starts a thread adding the dicom files.
void OnStartDicomImport(const QStringList &dicomData);
/// @brief Called when the selection in the series table has changed
void OnSeriesSelectionChanged(const QStringList &);
protected:
void SetDatabase(QString databaseFile);
bool DeletePatients();
bool DeleteStudies();
bool DeleteSeries();
ctkDICOMDatabase *m_LocalDatabase;
ctkDICOMIndexer *m_LocalIndexer;
Ui::QmitkDicomLocalStorageWidgetControls *m_Controls;
};
#endif
diff --git a/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
index a1b875db38..b7732f23f7 100644
--- a/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
+++ b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
@@ -1,105 +1,105 @@
/*============================================================================
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 mitkAffineBaseDataInteractor3D_h
#define mitkAffineBaseDataInteractor3D_h
#include "MitkDataTypesExtExports.h"
#include <mitkDataInteractor.h>
#include <mitkGeometry3D.h>
namespace mitk
{
////create events for interactions
#pragma GCC visibility push(default)
itkEventMacroDeclaration(AffineInteractionEvent, itk::AnyEvent);
itkEventMacroDeclaration(ScaleEvent, AffineInteractionEvent);
itkEventMacroDeclaration(RotateEvent, AffineInteractionEvent);
itkEventMacroDeclaration(TranslateEvent, AffineInteractionEvent);
#pragma GCC visibility pop
/**
* \brief Affine interaction with mitk::BaseGeometry.
*
* \ingroup Interaction
*/
// Inherit from DataInteractor, this provides functionality of a state machine and configurable inputs.
class MITKDATATYPESEXT_EXPORT AffineBaseDataInteractor3D : public DataInteractor
{
public:
mitkClassMacro(AffineBaseDataInteractor3D, DataInteractor);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void SetDataNode(DataNode *node) override;
void TranslateGeometry(mitk::Vector3D translate, mitk::BaseGeometry *geometry);
void RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry *geometry);
void ScaleGeometry(mitk::Point3D newScale, mitk::BaseGeometry *geometry);
mitk::BaseGeometry *GetUpdatedTimeGeometry(mitk::InteractionEvent *interactionEvent);
protected:
AffineBaseDataInteractor3D();
~AffineBaseDataInteractor3D() override;
/**
* Here actions strings from the loaded state machine pattern are mapped to functions of
* the DataInteractor. These functions are called when an action from the state machine pattern is executed.
*/
void ConnectActionsAndFunctions() override;
/**
* This function is called when a DataNode has been set/changed.
*/
void DataNodeChanged() override;
/**
* Initializes the movement, stores starting position.
*/
virtual bool CheckOverObject(const InteractionEvent *);
virtual void SelectObject(StateMachineAction *, InteractionEvent *);
virtual void DeselectObject(StateMachineAction *, InteractionEvent *);
virtual void InitTranslate(StateMachineAction *, InteractionEvent *);
virtual void InitRotate(StateMachineAction *, InteractionEvent *);
virtual void TranslateObject(StateMachineAction *, InteractionEvent *);
virtual void RotateObject(StateMachineAction *, InteractionEvent *);
virtual void ScaleObject(StateMachineAction *, InteractionEvent *);
virtual void TranslateUpKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateDownKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateLeftKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateRightKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateUpModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void TranslateDownModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateUpKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateDownKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateLeftKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateRightKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateUpModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void RotateDownModifierKey(StateMachineAction *, InteractionEvent *interactionEvent);
virtual void ScaleDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent);
virtual void ScaleUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent);
virtual void RestoreNodeProperties();
/**
- * @brief InitMembers convinience method to avoid code duplication between InitRotate() and InitTranslate().
+ * @brief InitMembers convenience method to avoid code duplication between InitRotate() and InitTranslate().
* @param interactionEvent
*/
bool InitMembers(InteractionEvent *interactionEvent);
private:
Point3D m_InitialPickedWorldPoint;
Point2D m_InitialPickedDisplayPoint;
Geometry3D::Pointer m_OriginalGeometry;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h b/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h
index d4ea0219e7..3c01d9bc53 100644
--- a/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h
+++ b/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h
@@ -1,88 +1,88 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkApplyDiffImageOperation_h
#define mitkApplyDiffImageOperation_h
#include "MitkDataTypesExtExports.h"
#include "mitkCompressedImageContainer.h"
#include "mitkOperation.h"
namespace mitk
{
/**
@brief Operation, that holds information about some image difference
This class stores undo information for DiffImageApplier.
Instances of this class are created e.g. by QmitkSlicesInterpolator.
This works only for images with 1 channel (gray scale images, no color images).
ApplyDiffImageOperation of course refers to an image (a segmentation usually).
- The refered image is observed for itk::DeleteEvent, because there is no SmartPointer
+ The referred image is observed for itk::DeleteEvent, because there is no SmartPointer
used to keep the image alive -- the purpose of this class is undo and the undo
stack should not keep things alive forever.
To save memory, compression is used via CompressedImageContainer.
@ingroup Undo
@ingroup ToolManagerEtAl
*/
class MITKDATATYPESEXT_EXPORT ApplyDiffImageOperation : public Operation
{
protected:
void OnImageDeleted();
Image *m_Image;
unsigned int m_SliceIndex;
unsigned int m_SliceDimension;
unsigned int m_TimeStep;
double m_Factor;
bool m_ImageStillValid;
unsigned long m_DeleteTag;
CompressedImageContainer m_CompressedImageContainer;
public:
/**
Pass only 2D images here.
\param operationType
\param image
\param diffImage
\param timeStep
\param sliceIndex brief Which slice to extract (first one has index 0).
\param sliceDimension Number of the dimension which is constant for all pixels of the desired slice (e.g. 0 for
axial)
*/
ApplyDiffImageOperation(OperationType operationType,
Image *image,
Image *diffImage,
unsigned int timeStep = 0,
unsigned int sliceDimension = 2,
unsigned int sliceIndex = 0);
~ApplyDiffImageOperation() override;
// Unfortunately cannot use itkGet/SetMacros here, since Operation does not inherit itk::Object
unsigned int GetSliceIndex() { return m_SliceIndex; }
unsigned int GetSliceDimension() { return m_SliceDimension; }
unsigned int GetTimeStep() { return m_TimeStep; }
void SetFactor(double factor) { m_Factor = factor; }
double GetFactor() { return m_Factor; }
Image *GetImage() { return m_Image; }
Image::Pointer GetDiffImage();
bool IsImageStillValid() { return m_ImageStillValid; }
};
} // namespace mitk
#endif
diff --git a/Modules/DataTypesExt/include/mitkColorSequence.h b/Modules/DataTypesExt/include/mitkColorSequence.h
index 2a8637e35d..37e48cb373 100644
--- a/Modules/DataTypesExt/include/mitkColorSequence.h
+++ b/Modules/DataTypesExt/include/mitkColorSequence.h
@@ -1,43 +1,43 @@
/*============================================================================
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 mitkColorSequence_h
#define mitkColorSequence_h
#include "MitkDataTypesExtExports.h"
#include <mitkColorProperty.h>
namespace mitk
{
/*!
- \brief Inferface for creating a sequence of nice/matching/appropriate/... colors.
+ \brief Interface for creating a sequence of nice/matching/appropriate/... colors.
See derived classes for implemented sequences.
*/
class MITKDATATYPESEXT_EXPORT ColorSequence
{
public:
ColorSequence();
virtual ~ColorSequence();
/*!
\brief Return another color
*/
virtual Color GetNextColor() = 0;
/*!
\brief Set the color-index to begin again
*/
virtual void GoToBegin() = 0;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h b/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h
index f95bb80f1e..ea3c65acea 100644
--- a/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h
+++ b/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h
@@ -1,79 +1,79 @@
/*============================================================================
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 mitkColorSequenceCycleH_h
#define mitkColorSequenceCycleH_h
#include "MitkDataTypesExtExports.h"
#include <mitkColorSequence.h>
namespace mitk
{
/*!
\brief Creates a list of around 36 different colors, where one is easily distinguished from the preceding one.
The list of colors starts with a fully saturated, full valued red (Hue = 0 = 360).
After that the sequence is generated like this:
- first cycle through fully saturated colors (increase hue by 60)
- then cycle through colors with halfed saturation (increase hue by 60)
- then cycle through colors with halfed value (increase hue by 60)
Finally repeat colors.
*/
class MITKDATATYPESEXT_EXPORT ColorSequenceCycleH : public ColorSequence
{
public:
ColorSequenceCycleH();
~ColorSequenceCycleH() override;
/*!
\brief Return another color
*/
Color GetNextColor() override;
/*!
\brief Rewind to first color
*/
void GoToBegin() override;
/*!
\brief Increase the used Hue value.
This can be done by steps ( = steps * 60 increase of Hue )
or absolute ( 0.0 < Hue < 360.0).
Can also be used to decrease the Hue; Values < 0 are cropped to 0.
Note: This does not change the other values, i.e. the color cycle.
Therefor, the method can just be used to skip steps (i.e. colors) in a cycle.
Use SetColorCycle if you want to change other values.
*/
virtual void ChangeHueValueByCycleSteps(int steps);
virtual void ChangeHueValueByAbsoluteNumber(float number);
/*!
\brief Set the color cycle.
The color cycle has to be an integer value between 0 and 5
(see class description for an explanation). Use this in combination with
- the hue value cahnge to generate your dream colors...
+ the hue value change to generate your dream colors...
*/
virtual void SetColorCycle(unsigned short cycle);
protected:
float color_h; // current hue (0 .. 360)
float color_s; // current saturation (0 .. 1)
float color_v; // current value (0 .. 1)
unsigned short color_cycle;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h b/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h
index 0c6fd009c3..b9e004cc75 100644
--- a/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h
+++ b/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h
@@ -1,112 +1,112 @@
/*============================================================================
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 mitkLabeledImageLookupTable_h
#define mitkLabeledImageLookupTable_h
#include "MitkDataTypesExtExports.h"
#include "mitkLevelWindow.h"
#include "mitkLookupTable.h"
#include <iostream>
#include <string>
namespace mitk
{
/**
* A lookup table for 2D mapping of labeled images. The lookup table supports
* images with up to 256 unsigned labels. Negative labels are not supported.
* Please use the level/window settings as given by the GetLevelWindow() method
* to make sure, that the colors are rendered correctly.
* The colors are initialized with random colors as default. As background
* the label 0 is assumed. The color for the background is set to fully transparent
* as default.
*/
class MITKDATATYPESEXT_EXPORT LabeledImageLookupTable : public LookupTable
{
public:
/**
* Standard mitk typedefs are generated by the mitkClassMacro
*/
mitkClassMacro(LabeledImageLookupTable, LookupTable);
/**
* Make this object constructable by the ::%New() Method.
*/
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* The data type for a label. Currently only images with labels
* in the range [0,255] are supported.
*/
typedef unsigned char LabelType;
LabeledImageLookupTable &operator=(const LookupTable &other) override;
/**
* Sets the color for a given label
* @param label The pixel value used as a label in the image
- * @param r The red component of the rgba color value. Values sould be given in the range [0,1]
- * @param g The green component of the rgba color value. Values sould be given in the range [0,1]
- * @param b The blue component of the rgba color value. Values sould be given in the range [0,1]
- * @param a The alpha component of the rgba color value. Values sould be given in the range [0,1]. Default is 1.
+ * @param r The red component of the rgba color value. Values should be given in the range [0,1]
+ * @param g The green component of the rgba color value. Values should be given in the range [0,1]
+ * @param b The blue component of the rgba color value. Values should be given in the range [0,1]
+ * @param a The alpha component of the rgba color value. Values should be given in the range [0,1]. Default is 1.
*/
virtual void SetColorForLabel(
const LabelType &label, const double &r, const double &g, const double &b, const double a = 1.0);
/**
* Determines the color which will be used for coloring a given label.
* @param label the label for which the color should be returned
* @returns an rgba array containing the color information for the given label.
* Color components are expressed as [0,1] double values.
*/
virtual double *GetColorForLabel(const LabelType &label);
/**
* Provides access to level window settings, which should be used
* in combination with the LUTs generated by this filter (at lease for
* 2D visualization. If you use other level/window settings, it is not
* guaranteed, that scalar values are mapped to the correct colors.
*/
mitk::LevelWindow GetLevelWindow() { return m_LevelWindow; }
protected:
/**
* Default constructor. Protected to prevent "normal" creation
*/
LabeledImageLookupTable();
LabeledImageLookupTable(const LabeledImageLookupTable &other);
/**
* Virtual destructor
*/
~LabeledImageLookupTable() override;
/**
* Generates a random rgb color value. Values for rgb are in the range
* [0,1]
*/
virtual void GenerateRandomColor(double &r, double &g, double &b);
/**
* Generates a radnom number drawn from a uniform
* distribution in the range [0,1].
*/
virtual double GenerateRandomNumber();
mitk::LevelWindow m_LevelWindow;
private:
itk::LightObject::Pointer InternalClone() const override;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkLookupTableSource.h b/Modules/DataTypesExt/include/mitkLookupTableSource.h
index 04e2021278..35072bdc10 100644
--- a/Modules/DataTypesExt/include/mitkLookupTableSource.h
+++ b/Modules/DataTypesExt/include/mitkLookupTableSource.h
@@ -1,89 +1,89 @@
/*============================================================================
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 mitkLookupTableSource_h
#define mitkLookupTableSource_h
#include "MitkDataTypesExtExports.h"
#include "mitkCommon.h"
#include "mitkLookupTable.h"
#include "itkProcessObject.h"
namespace mitk
{
/**
* @brief Base class for all objects which have an object of type
* mitkLookupTable as output
*
* Base class for all objects which have an object of type mitkLookupTable
* as output. It is assumed, that mitkLookupTableSources do not provide support
* for streaming, that is, that the requested region is always the largest
* possible region.
* @ingroup Process
*/
class MITKDATATYPESEXT_EXPORT LookupTableSource : public itk::ProcessObject
{
public:
mitkClassMacroItkParent(LookupTableSource, itk::ProcessObject);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::LookupTable OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef itk::DataObject::Pointer DataObjectPointer;
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
/**
* Generates the input requested region simply by calling the equivalent
* method of the superclass.
*/
void GenerateInputRequestedRegion() override;
/**
- * Replacement of the SetOutput method. I think it is not yet correcly
+ * Replacement of the SetOutput method. I think it is not yet correctly
* implemented, so you should better not use it.
- * @todo provide a more usefule implementation
+ * @todo provide a more useful implementation
* @param output the intended output of the lookup table source.
*/
virtual void GraftOutput(OutputType *output);
virtual OutputType *GetOutput();
virtual const OutputType *GetOutput() const;
virtual OutputType *GetOutput(DataObjectPointerArraySizeType idx);
virtual const OutputType *GetOutput(DataObjectPointerArraySizeType idx) const;
protected:
LookupTableSource();
~LookupTableSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h b/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h
index 08828ab4de..0bfa04e070 100644
--- a/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h
+++ b/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h
@@ -1,68 +1,68 @@
/*============================================================================
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 mitkUnstructuredGridSource_h
#define mitkUnstructuredGridSource_h
#include "MitkDataTypesExtExports.h"
#include "mitkBaseDataSource.h"
namespace mitk
{
class UnstructuredGrid;
//##Documentation
//## @brief Superclass of all classes generating unstructured grids (instances of class
//## UnstructuredGrid) 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::UnstructuredGridSource.
//## @ingroup Process
class MITKDATATYPESEXT_EXPORT UnstructuredGridSource : public BaseDataSource
{
public:
mitkClassMacro(UnstructuredGridSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::UnstructuredGrid OutputType;
mitkBaseDataSourceGetOutputDeclarations
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
UnstructuredGridSource();
~UnstructuredGridSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/DataTypesExt/include/mitkVideoSource.h b/Modules/DataTypesExt/include/mitkVideoSource.h
index b69375b056..a7149e2074 100644
--- a/Modules/DataTypesExt/include/mitkVideoSource.h
+++ b/Modules/DataTypesExt/include/mitkVideoSource.h
@@ -1,136 +1,136 @@
/*============================================================================
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 mitkVideoSource_h
#define mitkVideoSource_h
#include "MitkDataTypesExtExports.h"
#include "mitkCommon.h"
#include <itkObject.h>
#include <itkObjectFactory.h>
#include <mitkMessage.h>
namespace mitk
{
///
/// Simple base class for acquiring video data.
///
class MITKDATATYPESEXT_EXPORT VideoSource : virtual public itk::Object
{
public:
///
/// Smart pointer defs
///
mitkClassMacroItkParent(VideoSource, itk::Object);
///
/// assigns the grabbing devices for acquiring the next frame.
/// in this base implementation it does nothing except incrementing
/// m_FrameCount
///
virtual void FetchFrame();
///
/// \return a pointer to the image data array for opengl rendering.
///
virtual unsigned char *GetVideoTexture() = 0;
///
/// advices this class to start the video capturing.
/// in this base implementation: toggles m_CapturingInProcess, resets m_FrameCount
/// *ATTENTION*: this should be also done in subclasses overwriting this method
///
virtual void StartCapturing();
///
/// advices this class to stop the video capturing.
/// in this base implementation: toggles m_CapturingInProcess, resets m_FrameCount
/// *ATTENTION*: this should be also done in subclasses overwriting this method
///
virtual void StopCapturing();
///
/// \return true if video capturing is active.
/// \see m_CapturingInProcess
///
virtual bool IsCapturingEnabled() const;
///
/// \return the current frame width (might be 0 if unknown)
///
virtual int GetImageWidth();
///
/// \return the current frame height (might be 0 if unknown)
///
virtual int GetImageHeight();
///
/// \return the current frame count
///
virtual unsigned long GetFrameCount() const;
///
/// \return true, if capturing is currently paused, false otherwise
///
virtual bool GetCapturePaused() const;
///
/// toggles m_CapturePaused
/// In Subclasses this function can be overwritten to take
/// measurs to provide a pause image, *BUT DO NOT FORGET TO
/// TOGGLE m_CapturePaused*
///
virtual void PauseCapturing();
protected:
///
/// init member
///
VideoSource();
///
/// deletes m_CurrentVideoTexture (if not 0)
///
~VideoSource() override;
protected:
///
/// finally this is what the video source must create: a video texture pointer
/// default: 0
///
unsigned char *m_CurrentVideoTexture;
///
/// should be filled when the first frame is available
/// default: 0
///
int m_CaptureWidth;
///
/// should be filled when the first frame is available
/// default: 0
///
int m_CaptureHeight;
///
/// saves if capturing is in procress
/// default: false
///
bool m_CapturingInProcess;
///
/// Saves the current frame count. Incremented in FetchFrame().
- /// Resetted to 0 when StartCapturing() or StopCapturing() is called.
+ /// Reset to 0 when StartCapturing() or StopCapturing() is called.
/// default: 0
///
unsigned long m_FrameCount;
///
/// Saves if the capturing is currently paused, i.e. this
/// will not fetch any further frames but provide the current
/// frame as long as m_CapturePaused is true
/// default: false
///
bool m_CapturePaused;
};
}
#endif
diff --git a/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp b/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp
index 2769e718fa..b63d5d4617 100644
--- a/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp
+++ b/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp
@@ -1,333 +1,333 @@
/*============================================================================
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 "mitkClippingPlaneInteractor3D.h"
#include <mitkInteractionConst.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkRotationOperation.h>
#include <mitkSurface.h>
#include <vtkCamera.h>
#include <vtkInteractorObserver.h>
#include <vtkInteractorStyle.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkRenderWindowInteractor.h>
mitk::ClippingPlaneInteractor3D::ClippingPlaneInteractor3D()
{
m_OriginalGeometry = Geometry3D::New();
// Initialize vector arithmetic
m_ObjectNormal[0] = 0.0;
m_ObjectNormal[1] = 0.0;
m_ObjectNormal[2] = 1.0;
}
mitk::ClippingPlaneInteractor3D::~ClippingPlaneInteractor3D()
{
}
void mitk::ClippingPlaneInteractor3D::ConnectActionsAndFunctions()
{
// **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually
// executing an action
CONNECT_CONDITION("isOverObject", CheckOverObject);
// **Function** in the statmachine patterns also referred to as **Actions**
CONNECT_FUNCTION("selectObject", SelectObject);
CONNECT_FUNCTION("deselectObject", DeselectObject);
CONNECT_FUNCTION("initTranslate", InitTranslate);
CONNECT_FUNCTION("initRotate", InitRotate);
CONNECT_FUNCTION("translateObject", TranslateObject);
CONNECT_FUNCTION("rotateObject", RotateObject);
}
void mitk::ClippingPlaneInteractor3D::DataNodeChanged()
{
}
bool mitk::ClippingPlaneInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
Point3D currentWorldPoint;
if (interactionEvent->GetSender()->PickObject(positionEvent->GetPointerPositionOnScreen(), currentWorldPoint) ==
this->GetDataNode())
return true;
return false;
}
void mitk::ClippingPlaneInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
node->SetColor(1.0, 0.0, 0.0);
- // Colorize surface / wireframe dependend on distance from picked point
+ // Colorize surface / wireframe dependent on distance from picked point
this->ColorizeSurface(interactionEvent->GetSender(), 0.0);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::ClippingPlaneInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
node->SetColor(1.0, 1.0, 1.0);
// Colorize surface / wireframe as inactive
this->ColorizeSurface(interactionEvent->GetSender(), -1.0);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::ClippingPlaneInteractor3D::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
m_InitialPickedDisplayPoint[0],
m_InitialPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
m_InitialPickedWorldPoint);
// Get the timestep to also support 3D+t
int timeStep = 0;
if ((interactionEvent->GetSender()) != nullptr)
timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// Make deep copy of current Geometry3D of the plane
this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date
m_OriginalGeometry =
static_cast<Geometry3D *>(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer());
}
void mitk::ClippingPlaneInteractor3D::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
m_InitialPickedDisplayPoint[0],
m_InitialPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
m_InitialPickedWorldPoint);
// Get the timestep to also support 3D+t
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// Make deep copy of current Geometry3D of the plane
this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date
m_OriginalGeometry =
static_cast<Geometry3D *>(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer());
}
void mitk::ClippingPlaneInteractor3D::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
double currentWorldPoint[4];
mitk::Point2D currentDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentDisplayPoint[0],
currentDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
currentWorldPoint);
Vector3D interactionMove;
interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2];
Point3D origin = m_OriginalGeometry->GetOrigin();
// Get the timestep to also support 3D+t
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// If data is an mitk::Surface, extract it
Surface::Pointer surface = dynamic_cast<Surface *>(this->GetDataNode()->GetData());
vtkPolyData *polyData = nullptr;
if (surface.IsNotNull())
{
polyData = surface->GetVtkPolyData(timeStep);
// Extract surface normal from surface (if existent, otherwise use default)
vtkPointData *pointData = polyData->GetPointData();
if (pointData != nullptr)
{
vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal");
if (normal != nullptr)
{
m_ObjectNormal[0] = normal->GetComponent(0, 0);
m_ObjectNormal[1] = normal->GetComponent(0, 1);
m_ObjectNormal[2] = normal->GetComponent(0, 2);
}
}
}
Vector3D transformedObjectNormal;
this->GetDataNode()->GetData()->GetGeometry(timeStep)->IndexToWorld(m_ObjectNormal, transformedObjectNormal);
this->GetDataNode()->GetData()->GetGeometry(timeStep)->SetOrigin(
origin + transformedObjectNormal * (interactionMove * transformedObjectNormal));
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::ClippingPlaneInteractor3D::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
double currentWorldPoint[4];
Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentPickedDisplayPoint[0],
currentPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
currentWorldPoint);
vtkCamera *camera = nullptr;
vtkRenderer *currentVtkRenderer = nullptr;
if ((interactionEvent->GetSender()) != nullptr)
{
vtkRenderWindow *renderWindow = interactionEvent->GetSender()->GetRenderWindow();
if (renderWindow != nullptr)
{
vtkRenderWindowInteractor *renderWindowInteractor = renderWindow->GetInteractor();
if (renderWindowInteractor != nullptr)
{
currentVtkRenderer = renderWindowInteractor->GetInteractorStyle()->GetCurrentRenderer();
if (currentVtkRenderer != nullptr)
camera = currentVtkRenderer->GetActiveCamera();
}
}
}
if (camera)
{
double vpn[3];
camera->GetViewPlaneNormal(vpn);
Vector3D viewPlaneNormal;
viewPlaneNormal[0] = vpn[0];
viewPlaneNormal[1] = vpn[1];
viewPlaneNormal[2] = vpn[2];
Vector3D interactionMove;
interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2];
if (interactionMove[0] == 0 && interactionMove[1] == 0 && interactionMove[2] == 0)
return;
Vector3D rotationAxis = itk::CrossProduct(viewPlaneNormal, interactionMove);
rotationAxis.Normalize();
int *size = currentVtkRenderer->GetSize();
double l2 = (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) *
(currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) +
(currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]) *
(currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]);
double rotationAngle = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1]));
// Use center of data bounding box as center of rotation
Point3D rotationCenter = m_OriginalGeometry->GetCenter();
int timeStep = 0;
if ((interactionEvent->GetSender()) != nullptr)
timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// Reset current Geometry3D to original state (pre-interaction) and
// apply rotation
RotationOperation op(OpROTATE, rotationCenter, rotationAxis, rotationAngle);
Geometry3D::Pointer newGeometry = static_cast<Geometry3D *>(m_OriginalGeometry->Clone().GetPointer());
newGeometry->ExecuteOperation(&op);
mitk::TimeGeometry::Pointer timeGeometry = this->GetDataNode()->GetData()->GetTimeGeometry();
if (timeGeometry.IsNotNull())
timeGeometry->SetTimeStepGeometry(newGeometry, timeStep);
RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void mitk::ClippingPlaneInteractor3D::ColorizeSurface(BaseRenderer::Pointer renderer, double scalar)
{
BaseData::Pointer data = this->GetDataNode()->GetData();
if (data.IsNull())
{
MITK_ERROR << "ClippingPlaneInteractor3D: No data object present!";
return;
}
// Get the timestep to also support 3D+t
int timeStep = 0;
if (renderer.IsNotNull())
timeStep = renderer->GetTimeStep(data);
// If data is an mitk::Surface, extract it
Surface::Pointer surface = dynamic_cast<Surface *>(data.GetPointer());
vtkPolyData *polyData = nullptr;
if (surface.IsNotNull())
polyData = surface->GetVtkPolyData(timeStep);
if (polyData == nullptr)
{
MITK_ERROR << "ClippingPlaneInteractor3D: No poly data present!";
return;
}
vtkPointData *pointData = polyData->GetPointData();
if (pointData == nullptr)
{
MITK_ERROR << "ClippingPlaneInteractor3D: No point data present!";
return;
}
vtkDataArray *scalars = pointData->GetScalars();
if (scalars == nullptr)
{
MITK_ERROR << "ClippingPlaneInteractor3D: No scalars for point data present!";
return;
}
for (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i)
{
scalars->SetComponent(i, 0, scalar);
}
polyData->Modified();
pointData->Update();
}
diff --git a/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp b/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp
index fd6214a6a5..dcb8634714 100644
--- a/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp
+++ b/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp
@@ -1,139 +1,139 @@
/*============================================================================
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 "mitkLabeledImageLookupTable.h"
#include <cstdlib>
#include <vtkLookupTable.h>
/**
* Default constructor. Protected to prevent "normal" creation
*/
mitk::LabeledImageLookupTable::LabeledImageLookupTable() : m_LevelWindow(128, 256)
{
if (m_LookupTable == nullptr)
{
itkWarningMacro(
"LookupTable is nullptr, it should have been initialized by the default constructor of mitk::LookupTable");
m_LookupTable = vtkLookupTable::New();
}
m_LookupTable->SetNumberOfTableValues(256);
// set the background to black and fully transparent
m_LookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0);
// initialize the remaining 255 colors with random values and
// an alpha value of 1.0
double r, g, b;
//
// Initialize the random number generator with an arbitrary seed.
// This way, the generated colors are random, but always the same...
std::srand(2);
for (vtkIdType index = 1; index < 256; ++index)
{
GenerateRandomColor(r, g, b);
m_LookupTable->SetTableValue(index, r, g, b);
}
// initialize the default level/window settings,
// which can be accessed via GetLevelWindow();
m_LevelWindow.SetRangeMinMax(0, 255);
m_LevelWindow.SetWindowBounds(0, 255);
m_LevelWindow.SetFixed(true);
}
mitk::LabeledImageLookupTable::LabeledImageLookupTable(const mitk::LabeledImageLookupTable &other)
: LookupTable(other), m_LevelWindow(other.m_LevelWindow)
{
}
/**
* Virtual destructor
*/
mitk::LabeledImageLookupTable::~LabeledImageLookupTable()
{
}
mitk::LabeledImageLookupTable &mitk::LabeledImageLookupTable::operator=(const mitk::LookupTable &other)
{
LookupTable::operator=(other);
if (const auto *lut = dynamic_cast<const LabeledImageLookupTable *>(&other))
{
this->m_LevelWindow = lut->m_LevelWindow;
}
return *this;
}
/**
* Sets the color for a given label
* @param label The pixel value used as a label in the image
- * @param r The red component of the rgba color value. Values sould be given in the range [0,1]
- * @param g The green component of the rgba color value. Values sould be given in the range [0,1]
- * @param b The blue component of the rgba color value. Values sould be given in the range [0,1]
- * @param a The alpha component of the rgba color value. Values sould be given in the range [0,1]. Default is 1.
+ * @param r The red component of the rgba color value. Values should be given in the range [0,1]
+ * @param g The green component of the rgba color value. Values should be given in the range [0,1]
+ * @param b The blue component of the rgba color value. Values should be given in the range [0,1]
+ * @param a The alpha component of the rgba color value. Values should be given in the range [0,1]. Default is 1.
*/
void mitk::LabeledImageLookupTable::SetColorForLabel(const mitk::LabeledImageLookupTable::LabelType &label,
const double &r,
const double &g,
const double &b,
const double a)
{
if (m_LookupTable == nullptr)
{
itkWarningMacro("LookupTable is nullptr, but it should have been initialized by the constructor");
return;
}
m_LookupTable->SetTableValue(label, r, g, b, a);
}
/**
* Determines the color which will be used for coloring a given label.
* @param label the label for which the color should be returned
* @returns an rgba array containing the color information for the given label
* Color components are expressed as [0,1] double values.
*/
double *mitk::LabeledImageLookupTable::GetColorForLabel(const mitk::LabeledImageLookupTable::LabelType &label)
{
if (m_LookupTable == nullptr)
{
itkWarningMacro("LookupTable is nullptr, but it should have been initialized by the constructor");
return nullptr;
}
return m_LookupTable->GetTableValue(label);
}
/**
* Generates a random rgb color value. Values for rgb are in the range
* [0,1]
*/
void mitk::LabeledImageLookupTable::GenerateRandomColor(double &r, double &g, double &b)
{
r = GenerateRandomNumber();
g = GenerateRandomNumber();
b = GenerateRandomNumber();
}
/**
* Generates a radnom number drawn from a uniform
* distribution in the range [0,1].
*/
double mitk::LabeledImageLookupTable::GenerateRandomNumber()
{
return (((double)(std::rand())) / ((double)(RAND_MAX)));
}
itk::LightObject::Pointer mitk::LabeledImageLookupTable::InternalClone() const
{
itk::LightObject::Pointer result(new Self(*this));
result->UnRegister();
return result;
}
diff --git a/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp b/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp
index 94248cdde3..8470604f00 100644
--- a/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp
+++ b/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp
@@ -1,301 +1,301 @@
/*============================================================================
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 "mitkSurfaceDeformationDataInteractor3D.h"
#include "mitkMouseWheelEvent.h"
#include <vtkInteractorObserver.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
mitk::SurfaceDeformationDataInteractor3D::SurfaceDeformationDataInteractor3D() : m_GaussSigma(30.0)
{
m_OriginalPolyData = vtkPolyData::New();
// Initialize vector arithmetic
m_ObjectNormal[0] = 0.0;
m_ObjectNormal[1] = 0.0;
m_ObjectNormal[2] = 1.0;
}
mitk::SurfaceDeformationDataInteractor3D::~SurfaceDeformationDataInteractor3D()
{
m_OriginalPolyData->Delete();
}
void mitk::SurfaceDeformationDataInteractor3D::ConnectActionsAndFunctions()
{
// **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before
// actually executing an action
CONNECT_CONDITION("isOverObject", CheckOverObject);
// **Function** in the statmachine patterns also referred to as **Actions**
CONNECT_FUNCTION("selectObject", SelectObject);
CONNECT_FUNCTION("deselectObject", DeselectObject);
CONNECT_FUNCTION("initDeformation", InitDeformation);
CONNECT_FUNCTION("deformObject", DeformObject);
CONNECT_FUNCTION("scaleRadius", ScaleRadius);
}
void mitk::SurfaceDeformationDataInteractor3D::DataNodeChanged()
{
if (this->GetDataNode() != nullptr)
{
m_Surface = dynamic_cast<Surface *>(this->GetDataNode()->GetData());
if (m_Surface == nullptr)
MITK_ERROR << "SurfaceDeformationDataInteractor3D::DataNodeChanged(): DataNode has to contain a surface.";
}
else
m_Surface = nullptr;
}
bool mitk::SurfaceDeformationDataInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
Point3D currentPickedPoint;
if (interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, currentPickedPoint) == this->GetDataNode())
{
// Colorized surface at current picked position
m_SurfaceColorizationCenter = currentPickedPoint;
return true;
}
return false;
}
void mitk::SurfaceDeformationDataInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
this->GetDataNode()->SetColor(1.0, 0.0, 0.0);
- // Colorize surface / wireframe dependend on distance from picked point
+ // Colorize surface / wireframe dependent on distance from picked point
this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
this->GetDataNode()->SetColor(1.0, 1.0, 1.0);
// Colorize surface / wireframe as inactive
this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_CONSTANT, -1.0);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::InitDeformation(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
// Store current picked point
mitk::Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, m_InitialPickedPoint);
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentPickedDisplayPoint[0],
currentPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
m_InitialPickedWorldPoint);
// Make deep copy of vtkPolyData interacted on
m_OriginalPolyData->DeepCopy(polyData);
}
void mitk::SurfaceDeformationDataInteractor3D::DeformObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep);
double currentWorldPoint[4];
mitk::Point2D currentDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentDisplayPoint[0],
currentDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
currentWorldPoint);
// Calculate mouse move in 3D space
Vector3D interactionMove;
interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2];
// Transform mouse move into geometry space
this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date
Vector3D interactionMoveIndex;
geometry->WorldToIndex(interactionMove, interactionMoveIndex);
// Get picked point and transform into local coordinates
Point3D pickedPoint;
geometry->WorldToIndex(m_InitialPickedPoint, pickedPoint);
Vector3D v1 = pickedPoint.GetVectorFromOrigin();
vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal");
if (normal != nullptr)
{
m_ObjectNormal[0] = normal->GetComponent(0, 0);
m_ObjectNormal[1] = normal->GetComponent(0, 1);
m_ObjectNormal[2] = normal->GetComponent(0, 2);
}
Vector3D v2 = m_ObjectNormal * (interactionMoveIndex * m_ObjectNormal);
vtkPoints *originalPoints = m_OriginalPolyData->GetPoints();
vtkPoints *deformedPoints = polyData->GetPoints();
double denom = m_GaussSigma * m_GaussSigma * 2;
double point[3];
for (vtkIdType i = 0; i < deformedPoints->GetNumberOfPoints(); ++i)
{
// Get original point
double *originalPoint = originalPoints->GetPoint(i);
Vector3D v0;
v0[0] = originalPoint[0];
v0[1] = originalPoint[1];
v0[2] = originalPoint[2];
// Calculate distance of this point from line through picked point
double d = itk::CrossProduct(m_ObjectNormal, (v1 - v0)).GetNorm();
Vector3D t = v2 * exp(-d * d / denom);
point[0] = originalPoint[0] + t[0];
point[1] = originalPoint[1] + t[1];
point[2] = originalPoint[2] + t[2];
deformedPoints->SetPoint(i, point);
}
// Make sure that surface is colorized at initial picked position as long as we are in deformation state
m_SurfaceColorizationCenter = m_InitialPickedPoint;
polyData->Modified();
m_Surface->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::ScaleRadius(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *wheelEvent = dynamic_cast<const MouseWheelEvent *>(interactionEvent);
if (wheelEvent == nullptr)
return;
m_GaussSigma += (double)(wheelEvent->GetWheelDelta()) / 20;
if (m_GaussSigma < 10.0)
{
m_GaussSigma = 10.0;
}
else if (m_GaussSigma > 128.0)
{
m_GaussSigma = 128.0;
}
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
- // Colorize surface / wireframe dependend on sigma and distance from picked point
+ // Colorize surface / wireframe dependent on sigma and distance from picked point
this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::ColorizeSurface(
vtkPolyData *polyData, int timeStep, const Point3D &pickedPoint, int mode, double scalar)
{
if (polyData == nullptr)
return;
vtkPoints *points = polyData->GetPoints();
vtkPointData *pointData = polyData->GetPointData();
if (pointData == nullptr)
return;
vtkDataArray *scalars = pointData->GetScalars();
if (scalars == nullptr)
return;
if (mode == COLORIZATION_GAUSS)
{
// Get picked point and transform into local coordinates
Point3D localPickedPoint;
BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep);
geometry->WorldToIndex(pickedPoint, localPickedPoint);
Vector3D v1 = localPickedPoint.GetVectorFromOrigin();
vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal");
if (normal != nullptr)
{
m_ObjectNormal[0] = normal->GetComponent(0, 0);
m_ObjectNormal[1] = normal->GetComponent(0, 1);
m_ObjectNormal[2] = normal->GetComponent(0, 2);
}
double denom = m_GaussSigma * m_GaussSigma * 2;
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i)
{
// Get original point
double *point = points->GetPoint(i);
Vector3D v0;
v0[0] = point[0];
v0[1] = point[1];
v0[2] = point[2];
// Calculate distance of this point from line through picked point
double d = itk::CrossProduct(m_ObjectNormal, (v1 - v0)).GetNorm();
double t = exp(-d * d / denom);
scalars->SetComponent(i, 0, t);
}
}
else if (mode == COLORIZATION_CONSTANT)
{
for (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i)
{
scalars->SetComponent(i, 0, scalar);
}
}
polyData->Modified();
pointData->Update();
}
diff --git a/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp b/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp
index 18c69db63b..191544b94a 100644
--- a/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp
+++ b/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp
@@ -1,91 +1,91 @@
/*============================================================================
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 <limits>
// MITK includes
#include "mitkColorSequenceRainbow.h"
// VTK includes
#include <vtkDebugLeaks.h>
class mitkColorSequenceRainbowTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkColorSequenceRainbowTestSuite);
MITK_TEST(GetNextColor_ReturnsADifferentColor);
MITK_TEST(GoToBegin_NextCallReturnsSameColorAsFirstCall);
MITK_TEST(GetNextColor_CanWrapAroundWithoutCrashing);
CPPUNIT_TEST_SUITE_END();
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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
{
}
void tearDown() override
{
}
void GetNextColor_ReturnsADifferentColor()
{
mitk::ColorSequenceRainbow rainbowColorSequence;
mitk::Color color1 = rainbowColorSequence.GetNextColor();
mitk::Color color2 = rainbowColorSequence.GetNextColor();
CPPUNIT_ASSERT_MESSAGE("Two consecutive colors are not equal.", color1 != color2);
}
void GoToBegin_NextCallReturnsSameColorAsFirstCall()
{
mitk::ColorSequenceRainbow rainbowColorSequence;
mitk::Color color1 = rainbowColorSequence.GetNextColor();
rainbowColorSequence.GoToBegin();
mitk::Color color2 = rainbowColorSequence.GetNextColor();
CPPUNIT_ASSERT_MESSAGE("GoToBegin is identical to beginning.", color1 == color2);
}
void GetNextColor_CanWrapAroundWithoutCrashing()
{
int counter = 0;
try
{
mitk::ColorSequenceRainbow rainbowColorSequence;
mitk::Color color1 = rainbowColorSequence.GetNextColor();
mitk::Color color2 = rainbowColorSequence.GetNextColor();
while (color1 != color2 && counter < std::numeric_limits<int>::max())
{
color2 = rainbowColorSequence.GetNextColor();
++counter;
}
}
catch (...)
{
CPPUNIT_FAIL("Exception during rainbow color sequence color generation");
}
CPPUNIT_ASSERT_MESSAGE("Error free wraparound achieved.", counter < std::numeric_limits<int>::max());
}
};
MITK_TEST_SUITE_REGISTRATION(mitkColorSequenceRainbow)
diff --git a/Modules/Gizmo/include/mitkGizmo.h b/Modules/Gizmo/include/mitkGizmo.h
index 69e2009ee6..2da31558f1 100644
--- a/Modules/Gizmo/include/mitkGizmo.h
+++ b/Modules/Gizmo/include/mitkGizmo.h
@@ -1,185 +1,185 @@
/*============================================================================
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 mitkGizmo_h
#define mitkGizmo_h
#include <mitkDataNode.h>
#include <mitkSurface.h>
#include <MitkGizmoExports.h>
namespace mitk
{
class DataStorage;
class GizmoRemover;
//! A geometry manipulation "gizmo".
//!
//! This class represents the principal axes of some arbitrary BaseGeometry.
//!
//! The visualization shows the three axes x, y, and z along with a orthogonal ring around them.
//! In its center, a small sphere is visualized.
//!
//! The class is intended to be visualized along with another data object that "owns" the followed
//! BaseGeometry. The Gizmo will automatically update itself to all modifications to the
//! followed base geometry. Interactive modifications to the geometry can thus be easily visualized.
//!
- //! The gizmo is definded by
+ //! The gizmo is defined by
//! - a center
//! - three axes for x, y, and z
//! - a radius
//!
//! The radius determines the size of the axes and the rings around them.
//!
//! A nice overview of similar / alternative representations can be found in
//! "Schmidt R, Singh K, and Balakrishnan R. Sketching and Composing Widgets for 3D Manipulation.
//! EUROGRAPHICS 2008"
//!
//! \sa GizmoInteractor3D
class MITKGIZMO_EXPORT Gizmo : public Surface
{
public:
//! Names for the three axes
enum AxisType
{
AxisX,
AxisY,
AxisZ
};
//! Names for the different parts of the gizmo
enum HandleType
{
MoveFreely, //< the central sphere
MoveAlongAxisX,
MoveAlongAxisY,
MoveAlongAxisZ,
RotateAroundAxisX,
RotateAroundAxisY,
RotateAroundAxisZ,
ScaleX,
ScaleY,
ScaleZ,
NoHandle //< to indicate picking failure
};
//! Conversion for any kind of logging/debug/... purposes
static std::string HandleTypeToString(HandleType type);
mitkClassMacro(Gizmo, Surface);
itkNewMacro(Gizmo);
itkGetConstMacro(Center, Point3D);
itkSetMacro(Center, Point3D);
itkGetConstMacro(AxisX, Vector3D);
itkSetMacro(AxisX, Vector3D);
itkGetConstMacro(AxisY, Vector3D);
itkSetMacro(AxisY, Vector3D);
itkGetConstMacro(AxisZ, Vector3D);
itkSetMacro(AxisZ, Vector3D);
itkGetConstMacro(Radius, Vector3D);
itkSetMacro(Radius, Vector3D);
itkGetConstMacro(AllowTranslation, bool);
itkSetMacro(AllowTranslation, bool);
itkBooleanMacro(AllowTranslation);
itkGetConstMacro(AllowRotation, bool);
itkSetMacro(AllowRotation, bool);
itkBooleanMacro(AllowRotation);
itkGetConstMacro(AllowScaling, bool);
itkSetMacro(AllowScaling, bool);
itkBooleanMacro(AllowScaling);
//! Return the longest of the three axes.
double GetLongestRadius() const;
//! Updates the representing surface object after changes to center, axes, or radius.
void UpdateRepresentation();
//! Setup the gizmo to follow any ModifiedEvents of the given geometry.
//! The object will adapt and update itself in function of the geometry's changes.
void FollowGeometry(BaseGeometry *geom);
//! The ITK callback to receive modified events of the followed geometry
void OnFollowedGeometryModified();
//! Determine the nature of the the given vertex id.
//! Can be used after picking a vertex id to determine what part of the
//! gizmo has been picked.
HandleType GetHandleFromPointID(vtkIdType id);
//! Determine the nature of the the given vertex data value.
//! Can be used after picking a vertex data value to determine what part of the
//! gizmo has been picked.
mitk::Gizmo::HandleType GetHandleFromPointDataValue(double value);
//! Convenience creation of a gizmo for given node
//! \param node The node holding the geometry to be visualized
//! \param storage The DataStorage where a node holding the gizmo
//! shall be added to (ignored when nullptr)
//!
- //! \return DataNode::Pointer containing the node used for vizualization of our gizmo
+ //! \return DataNode::Pointer containing the node used for visualization of our gizmo
static DataNode::Pointer AddGizmoToNode(DataNode *node, DataStorage *storage);
//! Convenience removal of gizmo from given node
//! \param node The node being currently manipulated
//! \param storage The DataStorage where the gizmo has been added to
//!
//! \return true if the gizmo has been found and removed successfully
//!
//! Make sure to pass the same parameters here that you provided to a
//! previous call to AddGizmoToNode.
//!
- //! \return DataNode::Pointer containing the node used for vizualization of our gizmo
+ //! \return DataNode::Pointer containing the node used for visualization of our gizmo
static bool RemoveGizmoFromNode(DataNode *node, DataStorage *storage);
//! \return whether given node in given storage has a gizmo attached.
static bool HasGizmoAttached(mitk::DataNode *node, DataStorage *storage);
protected:
Gizmo();
~Gizmo() override;
Gizmo(const Gizmo &); // = delete;
Gizmo &operator=(const Gizmo &); // = delete;
//! Creates a vtkPolyData representing the parameters defining the gizmo.
vtkSmartPointer<vtkPolyData> BuildGizmo();
private:
Point3D m_Center;
Vector3D m_AxisX;
Vector3D m_AxisY;
Vector3D m_AxisZ;
Vector3D m_Radius;
bool m_AllowTranslation;
bool m_AllowRotation;
bool m_AllowScaling;
BaseGeometry::Pointer m_FollowedGeometry;
//! ITK tag for the observing of m_FollowedGeometry
unsigned long m_FollowerTag;
//! Observes a data storage for removal of the manipulated object.
//! Removes gizmo together with the manipulated object
std::unique_ptr<GizmoRemover> m_GizmoRemover;
};
}
#endif
diff --git a/Modules/GraphAlgorithms/itkShortestPathImageFilter.h b/Modules/GraphAlgorithms/itkShortestPathImageFilter.h
index 5d0aa957bc..abf640627f 100644
--- a/Modules/GraphAlgorithms/itkShortestPathImageFilter.h
+++ b/Modules/GraphAlgorithms/itkShortestPathImageFilter.h
@@ -1,235 +1,235 @@
/*============================================================================
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 __itkShortestPathImageFilter_h
#define __itkShortestPathImageFilter_h
#include "itkImageToImageFilter.h"
#include "itkShortestPathCostFunction.h"
#include "itkShortestPathNode.h"
#include <itkImageRegionIteratorWithIndex.h>
#include <itkMacro.h>
// ------- INFORMATION ----------
/// SET FUNCTIONS
// void SetInput( ItkImage ) // Compulsory
// void SetStartIndex (const IndexType & StartIndex); // Compulsory
// void SetEndIndex(const IndexType & EndIndex); // Compulsory
// void SetFullNeighborsMode(bool) // Optional (default=false), if false N4, if true N26
// void SetActivateTimeOut(bool) // Optional (default=false), for debug issues: after 30s algorithms terminates. You can
// have a look at the VectorOrderImage to see how far it came
// void SetMakeOutputImage(bool) // Optional (default=true), Generate an outputimage of the path. You can also get the
// path directoy with GetVectorPath()
// void SetCalcAllDistances(bool) // Optional (default=false), Calculate Distances over the whole image. CAREFUL,
// algorithm time extends a lot. Necessary for GetDistanceImage
// void SetStoreVectorOrder(bool) // Optional (default=false), Stores in which order the pixels were checked. Necessary
// for GetVectorOrderImage
// void AddEndIndex(const IndexType & EndIndex) //Optional. By calling this function you can add several endpoints! The
-// algorithm will look for several shortest Pathes. From Start to all Endpoints.
+// algorithm will look for several shortest Paths. From Start to all Endpoints.
//
/// GET FUNCTIONS
// std::vector< itk::Index<3> > GetVectorPath(); // returns the shortest path as vector
-// std::vector< std::vector< itk::Index<3> > GetMultipleVectorPathe(); // returns a vector of shortest Pathes (which are
+// std::vector< std::vector< itk::Index<3> > GetMultipleVectorPathe(); // returns a vector of shortest Paths (which are
// vectors of points)
// GetDistanceImage // Returns the distance image
// GetVectorOrderIMage // Returns the Vector Order image
//
// EXAMPLE USE
// pleae see qmitkmitralvalvesegmentation4dtee bundle
namespace itk
{
template <class TInputImageType, class TOutputImageType>
class ShortestPathImageFilter : public ImageToImageFilter<TInputImageType, TOutputImageType>
{
public:
// Standard Typedefs
typedef ShortestPathImageFilter Self;
typedef ImageToImageFilter<TInputImageType, TOutputImageType> Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
// Typdefs for metric
typedef ShortestPathCostFunction<TInputImageType> CostFunctionType;
typedef typename CostFunctionType::Pointer CostFunctionTypePointer;
// More typdefs for convenience
typedef TInputImageType InputImageType;
typedef typename TInputImageType::Pointer InputImagePointer;
typedef typename TInputImageType::PixelType InputImagePixelType;
typedef typename TInputImageType::SizeType InputImageSizeType;
typedef typename TInputImageType::IndexType IndexType;
typedef typename itk::ImageRegionIteratorWithIndex<InputImageType> InputImageIteratorType;
typedef TOutputImageType OutputImageType;
typedef typename TOutputImageType::Pointer OutputImagePointer;
typedef typename TOutputImageType::PixelType OutputImagePixelType;
typedef typename TOutputImageType::IndexType OutputImageIndexType;
typedef ImageRegionIteratorWithIndex<OutputImageType> OutputImageIteratorType;
typedef itk::ShapedNeighborhoodIterator<TInputImageType> itkShapedNeighborhoodIteratorType;
// New Macro for smartpointer instantiation
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
// Run-time type information
itkTypeMacro(ShortestPathImageFilter, ImageToImageFilter);
// Display
void PrintSelf(std::ostream &os, Indent indent) const override;
// Compare function for A_STAR
struct CompareNodeStar
{
bool operator()(ShortestPathNode *a, ShortestPathNode *b) { return (a->distAndEst > b->distAndEst); }
};
// \brief Set Starpoint for ShortestPath Calculation
void SetStartIndex(const IndexType &StartIndex);
// \brief Adds Endpoint for multiple ShortestPath Calculation
void AddEndIndex(const IndexType &index);
// \brief Set Endpoint for ShortestPath Calculation
void SetEndIndex(const IndexType &EndIndex);
// \brief Set FullNeighborsMode. false = no diagonal neighbors, in 2D this means N4 Neigborhood. true = would be N8
// in 2D
itkSetMacro(FullNeighborsMode, bool);
itkGetMacro(FullNeighborsMode, bool);
// \brief Set Graph_fullNeighbors. false = no diagonal neighbors, in 2D this means N4 Neigborhood. true = would be
// N8 in 2D
itkSetMacro(Graph_fullNeighbors, bool);
// \brief (default=true), Produce output image, which shows the shortest path. But you can also get the shortest
// Path directly as vector with the function GetVectorPath
itkSetMacro(MakeOutputImage, bool);
itkGetMacro(MakeOutputImage, bool);
// \brief (default=false), Store an Vector of Order, so you can call getVectorOrderImage after update
itkSetMacro(StoreVectorOrder, bool);
itkGetMacro(StoreVectorOrder, bool);
// \brief (default=false), // Calculate all Distances to all pixels, so you can call getDistanceImage after update
// (warning algo will take a long time)
itkSetMacro(CalcAllDistances, bool);
itkGetMacro(CalcAllDistances, bool);
// \brief (default=false), for debug issues: after 30s algorithms terminates. You can have a look at the
// VectorOrderImage to see how far it came
itkSetMacro(ActivateTimeOut, bool);
itkGetMacro(ActivateTimeOut, bool);
// \brief returns shortest Path as vector
std::vector<IndexType> GetVectorPath();
// \brief returns Multiple shortest Paths. You can call this function, when u performed a multiple shortest path
// search (one start, several ends)
std::vector<std::vector<IndexType>> GetMultipleVectorPaths();
// \brief returns the vector order image. It shows in which order the pixels were checked. good for debugging. Be
// sure to have m_StoreVectorOrder=true
OutputImagePointer GetVectorOrderImage();
// \brief returns the distance image. It shows the distances from the startpoint to all other pixels. Be sure to
// have m_CalcAllDistances=true
OutputImagePointer GetDistanceImage();
// \brief Fill m_VectorPath
void MakeShortestPathVector();
// \brief cleans up the filter
void CleanUp();
itkSetObjectMacro(CostFunction,
CostFunctionType); // itkSetObjectMacro = set function that uses pointer as parameter
itkGetObjectMacro(CostFunction, CostFunctionType);
void SetUseCostFunction(bool doUseCostFunction) { m_useCostFunction = doUseCostFunction; };
bool GetUseCostFunction() { return m_useCostFunction; };
protected:
std::vector<IndexType>
m_endPoints; // if you fill this vector, the algo will not rest until all endPoints have been reached
std::vector<IndexType> m_endPointsClosed;
ShortestPathNode *m_Nodes; // main list that contains all nodes
NodeNumType m_Graph_NumberOfNodes;
NodeNumType m_Graph_StartNode;
NodeNumType m_Graph_EndNode;
bool m_Graph_fullNeighbors;
bool m_useCostFunction;
std::vector<ShortestPathNode *> m_Graph_DiscoveredNodeList;
ShortestPathImageFilter(Self &); // intentionally not implemented
void operator=(const Self &); // intentionally not implemented
const static int BACKGROUND = 0;
const static int FOREGROUND = 255;
bool m_FullNeighborsMode;
bool m_MakeOutputImage;
bool m_StoreVectorOrder; // Store an Vector of Order, so you can call getVectorOrderImage after update
bool m_CalcAllDistances; // Calculate all Distances, so you can call getDistanceImage after update (warning algo
// will take a long time)
bool multipleEndPoints;
bool m_ActivateTimeOut; // if true, then i search max. 30 secs. then abort
bool m_Initialized;
CostFunctionTypePointer m_CostFunction;
IndexType m_StartIndex, m_EndIndex;
std::vector<IndexType> m_VectorPath;
std::vector<std::vector<IndexType>> m_MultipleVectorPaths;
std::vector<NodeNumType> m_VectorOrder;
ShortestPathImageFilter();
~ShortestPathImageFilter() override;
// \brief Create all the outputs
void MakeOutputs();
// \brief Generate Data
void GenerateData() override;
// \brief gets the estimate costs from pixel a to target.
double getEstimatedCostsToTarget(const IndexType &a);
typename InputImageType::Pointer m_magnitudeImage;
// \brief Convert a indexnumber of a node in m_Nodes to image coordinates
typename TInputImageType::IndexType NodeToCoord(NodeNumType);
// \brief Convert image coordinate to a indexnumber of a node in m_Nodes
unsigned int CoordToNode(IndexType);
// \brief Returns the neighbors of a node
std::vector<ShortestPathNode *> GetNeighbors(NodeNumType nodeNum, bool FullNeighbors);
// \brief Check if coords are in bounds of image
bool CoordIsInBounds(IndexType);
// \brief Initializes the graph
void InitGraph();
// \brief Start ShortestPathSearch
void StartShortestPathSearch();
};
} // end of namespace itk
#include "itkShortestPathImageFilter.txx"
#endif
diff --git a/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx b/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx
index edaba4950e..6c010a9423 100644
--- a/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx
+++ b/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx
@@ -1,889 +1,889 @@
/*============================================================================
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 __itkShortestPathImageFilter_txx
#define __itkShortestPathImageFilter_txx
#include "itkShortestPathImageFilter.h"
#include "mitkMemoryUtilities.h"
#include <ctime>
#include <algorithm>
#include <iostream>
#include <vector>
namespace itk
{
// Constructor (initialize standard values)
template <class TInputImageType, class TOutputImageType>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::ShortestPathImageFilter()
: m_Nodes(nullptr),
m_Graph_NumberOfNodes(0),
m_Graph_fullNeighbors(false),
m_useCostFunction(true),
m_FullNeighborsMode(false),
m_MakeOutputImage(true),
m_StoreVectorOrder(false),
m_CalcAllDistances(false),
multipleEndPoints(false),
m_ActivateTimeOut(false),
m_Initialized(false)
{
m_endPoints.clear();
m_endPointsClosed.clear();
if (m_MakeOutputImage)
{
this->SetNumberOfRequiredOutputs(1);
this->SetNthOutput(0, OutputImageType::New());
}
}
template <class TInputImageType, class TOutputImageType>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::~ShortestPathImageFilter()
{
delete[] m_Nodes;
}
template <class TInputImageType, class TOutputImageType>
inline typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::IndexType
ShortestPathImageFilter<TInputImageType, TOutputImageType>::NodeToCoord(NodeNumType node)
{
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
int dim = InputImageType::ImageDimension;
IndexType coord;
if (dim == 2)
{
coord[1] = node / size[0];
coord[0] = node % size[0];
if (((unsigned long)coord[0] >= size[0]) || ((unsigned long)coord[1] >= size[1]))
{
coord[0] = 0;
coord[1] = 0;
}
}
if (dim == 3)
{
coord[2] = node / (size[0] * size[1]);
coord[1] = (node % (size[0] * size[1])) / size[0];
coord[0] = (node % (size[0] * size[1])) % size[0];
if (((unsigned long)coord[0] >= size[0]) || ((unsigned long)coord[1] >= size[1]) ||
((unsigned long)coord[2] >= size[2]))
{
coord[0] = 0;
coord[1] = 0;
coord[2] = 0;
}
}
return coord;
}
template <class TInputImageType, class TOutputImageType>
inline typename itk::NodeNumType ShortestPathImageFilter<TInputImageType, TOutputImageType>::CoordToNode(
IndexType coord)
{
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
int dim = InputImageType::ImageDimension;
NodeNumType node = 0;
if (dim == 2)
{
node = (coord[1] * size[0]) + coord[0];
}
if (dim == 3)
{
node = (coord[2] * size[0] * size[1]) + (coord[1] * size[0]) + coord[0];
}
if ((m_Graph_NumberOfNodes > 0) && (node >= m_Graph_NumberOfNodes))
{
/*MITK_INFO << "WARNING! Coordinates outside image!";
MITK_INFO << "Coords = " << coord ;
MITK_INFO << "ImageDim = " << dim ;
MITK_INFO << "RequestedRegionSize = " << size ;*/
node = 0;
}
return node;
}
template <class TInputImageType, class TOutputImageType>
inline bool ShortestPathImageFilter<TInputImageType, TOutputImageType>::CoordIsInBounds(IndexType coord)
{
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
int dim = InputImageType::ImageDimension;
if (dim == 2)
{
if ((coord[0] >= 0) && ((unsigned long)coord[0] < size[0]) && (coord[1] >= 0) &&
((unsigned long)coord[1] < size[1]))
{
return true;
}
}
if (dim == 3)
{
if ((coord[0] >= 0) && ((unsigned long)coord[0] < size[0]) && (coord[1] >= 0) &&
((unsigned long)coord[1] < size[1]) && (coord[2] >= 0) && ((unsigned long)coord[2] < size[2]))
{
return true;
}
}
return false;
}
template <class TInputImageType, class TOutputImageType>
inline std::vector<ShortestPathNode *> ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetNeighbors(
unsigned int nodeNum, bool FullNeighbors)
{
// returns a vector of nodepointers.. these nodes are the neighbors
int dim = InputImageType::ImageDimension;
IndexType Coord = NodeToCoord(nodeNum);
IndexType NeighborCoord;
std::vector<ShortestPathNode *> nodeList;
int neighborDistance = 1; // if i increase that, i might not hit the endnote
// maybe use itkNeighborhoodIterator here, might be faster
if (dim == 2)
{
// N4
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
if (FullNeighbors)
{
// N8
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
}
}
if (dim == 3)
{
// N6
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
if (FullNeighbors)
{
// N26
// Middle Slice
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// BackSlice (Diagonal)
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// BackSlice (Non-Diag)
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// FrontSlice (Diagonal)
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// FrontSlice(Non-Diag)
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
}
}
return nodeList;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::SetStartIndex(
const typename TInputImageType::IndexType &StartIndex)
{
for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i)
{
m_StartIndex[i] = StartIndex[i];
}
m_Graph_StartNode = CoordToNode(m_StartIndex);
// MITK_INFO << "StartIndex = " << StartIndex;
// MITK_INFO << "StartNode = " << m_Graph_StartNode;
m_Initialized = false;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::SetEndIndex(
const typename TInputImageType::IndexType &EndIndex)
{
for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i)
{
m_EndIndex[i] = EndIndex[i];
}
m_Graph_EndNode = CoordToNode(m_EndIndex);
// MITK_INFO << "EndNode = " << m_Graph_EndNode;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::AddEndIndex(
const typename TInputImageType::IndexType &index)
{
// ONLY FOR MULTIPLE END POINTS SEARCH
IndexType newEndIndex;
for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i)
{
newEndIndex[i] = index[i];
}
m_endPoints.push_back(newEndIndex);
SetEndIndex(m_endPoints[0]);
multipleEndPoints = true;
}
template <class TInputImageType, class TOutputImageType>
inline double ShortestPathImageFilter<TInputImageType, TOutputImageType>::getEstimatedCostsToTarget(
const typename TInputImageType::IndexType &a)
{
// Returns the minimal possible costs for a path from "a" to targetnode.
itk::Vector<float, 3> v;
v[0] = m_EndIndex[0] - a[0];
v[1] = m_EndIndex[1] - a[1];
v[2] = m_EndIndex[2] - a[2];
return m_CostFunction->GetMinCost() * v.GetNorm();
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::InitGraph()
{
if (!m_Initialized)
{
// Clean up previous stuff
CleanUp();
// Calc Number of nodes
auto imageDimensions = TInputImageType::ImageDimension;
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
m_Graph_NumberOfNodes = 1;
for (NodeNumType i = 0; i < imageDimensions; ++i)
m_Graph_NumberOfNodes = m_Graph_NumberOfNodes * size[i];
// Initialize mainNodeList with that number
m_Nodes = new ShortestPathNode[m_Graph_NumberOfNodes];
// Initialize each node in nodelist
for (NodeNumType i = 0; i < m_Graph_NumberOfNodes; i++)
{
m_Nodes[i].distAndEst = -1;
m_Nodes[i].distance = -1;
m_Nodes[i].prevNode = -1;
m_Nodes[i].mainListIndex = i;
m_Nodes[i].closed = false;
}
m_Initialized = true;
}
// In the beginning, the Startnode needs a distance of 0
m_Nodes[m_Graph_StartNode].distance = 0;
m_Nodes[m_Graph_StartNode].distAndEst = 0;
- // initalize cost function
+ // initialize cost function
m_CostFunction->Initialize();
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::StartShortestPathSearch()
{
// Setup Timer
clock_t startAll = clock();
clock_t stopAll = clock();
// init variables
double durationAll = 0;
bool timeout = false;
NodeNumType mainNodeListIndex = 0;
DistanceType curNodeDistance = 0;
// Create Multimap (tree structure for fast searching)
std::multimap<double, ShortestPathNode *> myMap;
std::pair<std::multimap<double, ShortestPathNode *>::iterator, std::multimap<double, ShortestPathNode *>::iterator>
ret;
std::multimap<double, ShortestPathNode *>::iterator it;
// At first, only startNote is discovered.
myMap.insert(
std::pair<double, ShortestPathNode *>(m_Nodes[m_Graph_StartNode].distAndEst, &m_Nodes[m_Graph_StartNode]));
// While there are discovered Nodes, pick the one with lowest distance,
// update its neighbors and eventually delete it from the discovered Nodes list.
while (!myMap.empty())
{
// Get element with lowest score
mainNodeListIndex = myMap.begin()->second->mainListIndex;
curNodeDistance = myMap.begin()->second->distance;
myMap.begin()->second->closed = true; // close it
// Debug:
// MITK_INFO << "INFO: size " << myMap.size();
/*
for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}
*/
// Kicks out element with lowest score
myMap.erase(myMap.begin());
// if wanted, store vector order
if (m_StoreVectorOrder)
{
m_VectorOrder.push_back(mainNodeListIndex);
}
// Check neighbors
std::vector<ShortestPathNode *> neighborNodes = GetNeighbors(mainNodeListIndex, m_Graph_fullNeighbors);
for (NodeNumType i = 0; i < neighborNodes.size(); i++)
{
if (neighborNodes[i]->closed)
continue; // this nodes is already closed, go to next neighbor
IndexType coordCurNode = NodeToCoord(mainNodeListIndex);
IndexType coordNeighborNode = NodeToCoord(neighborNodes[i]->mainListIndex);
// calculate the new Distance to the current neighbor
double newDistance = curNodeDistance + (m_CostFunction->GetCost(coordCurNode, coordNeighborNode));
// if it is shorter than any yet known path to this neighbor, than the current path is better. Save that!
if ((newDistance < neighborNodes[i]->distance) || (neighborNodes[i]->distance == -1))
{
// if that neighbornode is not in discoverednodeList yet, Push it there and update
if (neighborNodes[i]->distance == -1)
{
neighborNodes[i]->distance = newDistance;
neighborNodes[i]->distAndEst = newDistance + getEstimatedCostsToTarget(coordNeighborNode);
neighborNodes[i]->prevNode = mainNodeListIndex;
myMap.insert(std::pair<double, ShortestPathNode *>(m_Nodes[neighborNodes[i]->mainListIndex].distAndEst,
&m_Nodes[neighborNodes[i]->mainListIndex]));
/*
MITK_INFO << "Inserted: " << m_Nodes[neighborNodes[i]->mainListIndex].distAndEst << "|" <<
m_Nodes[neighborNodes[i]->mainListIndex].mainListIndex;
MITK_INFO << "INFO: size " << myMap.size();
for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}
*/
}
// or if is already in discoverednodelist, update
else
{
/*
it = myMap.find(neighborNodes[i]->distAndEst);
if (it == myMap.end() )
{
MITK_INFO << "Nothing!";
// look further
for (it = myMap.begin(); it != myMap.end(); ++it)
{
if ((*it).second->mainListIndex == lookForId)
{
MITK_INFO << "But it is there!!!";
MITK_INFO << "Searched for: " << lookFor << " but had: " << (*it).second->distAndEst;
}
}
}
*/
// 1st : find and delete old element
bool found = false;
ret = myMap.equal_range(neighborNodes[i]->distAndEst);
if ((ret.first == ret.second))
{
/*+++++++++++++ no exact match +++++++++++++*/
// MITK_INFO << "No exact match!"; // if this happens, you are screwed
/*
MITK_INFO << "Was looking for: " << lookFor << " ID: " << lookForId;
if (ret.first != myMap.end() )
{
it = ret.first;
MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex;
++it;
MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex;
--it;
--it;
MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex;
}
// look if that ID is found in the map
for (it = myMap.begin(); it != myMap.end(); ++it)
{
if ((*it).second->mainListIndex == lookForId)
{
MITK_INFO << "But it is there!!!";
MITK_INFO << "Searched dist: " << lookFor << " found dist: " << (*it).second->distAndEst;
}
}
*/
}
else
{
for (it = ret.first; it != ret.second; ++it)
{
if (it->second->mainListIndex == neighborNodes[i]->mainListIndex)
{
found = true;
myMap.erase(it);
/*
MITK_INFO << "INFO: size " << myMap.size();
MITK_INFO << "Erase: " << it->first << "|" << it->second->mainListIndex;
MITK_INFO << "INFO: size " << myMap.size();
for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}
*/
break;
}
}
}
if (!found)
{
// MITK_INFO << "Could not find it! :(";
continue;
}
// 2nd: update and insert new element
neighborNodes[i]->distance = newDistance;
neighborNodes[i]->distAndEst = newDistance + getEstimatedCostsToTarget(coordNeighborNode);
neighborNodes[i]->prevNode = mainNodeListIndex;
// myMap.insert( std::pair<double,ShortestPathNode*> (neighborNodes[i]->distAndEst, neighborNodes[i]));
myMap.insert(std::pair<double, ShortestPathNode *>(m_Nodes[neighborNodes[i]->mainListIndex].distAndEst,
&m_Nodes[neighborNodes[i]->mainListIndex]));
// MITK_INFO << "Re-Inserted: " << m_Nodes[neighborNodes[i]->mainListIndex].distAndEst << "|" <<
// m_Nodes[neighborNodes[i]->mainListIndex].mainListIndex;
// MITK_INFO << "INFO: size " << myMap.size();
/*for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}*/
}
}
}
// finished with checking all neighbors.
// Check Timeout, if activated
if (m_ActivateTimeOut)
{
stopAll = clock();
durationAll = (double)(stopAll - startAll) / CLOCKS_PER_SEC;
if (durationAll >= 30)
{
// MITK_INFO << "TIMEOUT!! Search took over 30 seconds";
timeout = true;
}
}
// Check end criteria:
// For multiple points
if (multipleEndPoints)
{
// super slow, make it faster
for (unsigned int i = 0; i < m_endPoints.size(); i++)
{
if (CoordToNode(m_endPoints[i]) == mainNodeListIndex)
{
m_endPointsClosed.push_back(NodeToCoord(mainNodeListIndex));
m_endPoints.erase(m_endPoints.begin() + i);
if (m_endPoints.empty())
{
// Finished! break
return;
}
if (m_Graph_EndNode == mainNodeListIndex)
{
// set new end
SetEndIndex(m_endPoints[0]);
}
}
}
}
// if single end point, then end, if this one is reached or timeout happened.
else if ((mainNodeListIndex == m_Graph_EndNode || timeout) && !m_CalcAllDistances)
{
/*if (m_StoreVectorOrder)
MITK_INFO << "Number of Nodes checked: " << m_VectorOrder.size() ;*/
return;
}
}
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::MakeOutputs()
{
// MITK_INFO << "Make Output";
if (m_MakeOutputImage)
{
OutputImagePointer output0 = this->GetOutput(0);
output0->SetRegions(this->GetInput()->GetLargestPossibleRegion());
output0->Allocate();
OutputImageIteratorType shortestPathImageIt(output0, output0->GetRequestedRegion());
// Create ShortestPathImage (Output 0)
for (shortestPathImageIt.GoToBegin(); !shortestPathImageIt.IsAtEnd(); ++shortestPathImageIt)
{
- // First intialize with background color
+ // First initialize with background color
shortestPathImageIt.Set(BACKGROUND);
}
if (!multipleEndPoints) // Only one path was calculated
{
for (unsigned int i = 0; i < m_VectorPath.size(); i++)
{
shortestPathImageIt.SetIndex(m_VectorPath[i]);
shortestPathImageIt.Set(FOREGROUND);
}
}
- else // multiple pathes has been calculated, draw all
+ else // multiple paths has been calculated, draw all
{
for (unsigned int i = 0; i < m_MultipleVectorPaths.size(); i++)
{
for (unsigned int j = 0; j < m_MultipleVectorPaths[i].size(); j++)
{
shortestPathImageIt.SetIndex(m_MultipleVectorPaths[i][j]);
shortestPathImageIt.Set(FOREGROUND);
}
}
}
}
}
template <class TInputImageType, class TOutputImageType>
typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::OutputImagePointer
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetVectorOrderImage()
{
// Create Vector Order Image
// Return it
OutputImagePointer image = OutputImageType::New();
image->SetRegions(this->GetInput()->GetLargestPossibleRegion());
image->Allocate();
OutputImageIteratorType vectorOrderImageIt(image, image->GetRequestedRegion());
// MITK_INFO << "GetVectorOrderImage";
for (vectorOrderImageIt.GoToBegin(); !vectorOrderImageIt.IsAtEnd(); ++vectorOrderImageIt)
{
- // First intialize with background color
+ // First initialize with background color
vectorOrderImageIt.Value() = BACKGROUND;
}
for (int i = 0; i < m_VectorOrder.size(); i++)
{
vectorOrderImageIt.SetIndex(NodeToCoord(m_VectorOrder[i]));
vectorOrderImageIt.Set(BACKGROUND + i);
}
return image;
}
template <class TInputImageType, class TOutputImageType>
typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::OutputImagePointer
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetDistanceImage()
{
// Create Distance Image
// Return it
OutputImagePointer image = OutputImageType::New();
image->SetRegions(this->GetInput()->GetLargestPossibleRegion());
image->Allocate();
;
OutputImageIteratorType distanceImageIt(image, image->GetRequestedRegion());
// Create Distance Image (Output 1)
NodeNumType myNodeNum;
for (distanceImageIt.GoToBegin(); !distanceImageIt.IsAtEnd(); ++distanceImageIt)
{
IndexType index = distanceImageIt.GetIndex();
myNodeNum = CoordToNode(index);
double newVal = m_Nodes[myNodeNum].distance;
distanceImageIt.Set(newVal);
}
}
template <class TInputImageType, class TOutputImageType>
std::vector<typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::IndexType>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetVectorPath()
{
return m_VectorPath;
}
template <class TInputImageType, class TOutputImageType>
std::vector<std::vector<typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::IndexType>>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetMultipleVectorPaths()
{
return m_MultipleVectorPaths;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::MakeShortestPathVector()
{
// MITK_INFO << "Make ShortestPath Vec";
if (m_useCostFunction == false)
{
m_VectorPath.push_back(NodeToCoord(m_Graph_StartNode));
m_VectorPath.push_back(NodeToCoord(m_Graph_EndNode));
return;
}
// single end point
if (!multipleEndPoints)
{
// fill m_VectorPath with the Shortest Path
m_VectorPath.clear();
// Go backwards from endnote to startnode
NodeNumType prevNode = m_Graph_EndNode;
while (prevNode != m_Graph_StartNode)
{
m_VectorPath.push_back(NodeToCoord(prevNode));
prevNode = m_Nodes[prevNode].prevNode;
}
m_VectorPath.push_back(NodeToCoord(prevNode));
// reverse it
std::reverse(m_VectorPath.begin(), m_VectorPath.end());
}
- // Multiple end end points and pathes
+ // Multiple end end points and paths
else
{
for (unsigned int i = 0; i < m_endPointsClosed.size(); i++)
{
m_VectorPath.clear();
// Go backwards from endnote to startnode
NodeNumType prevNode = CoordToNode(m_endPointsClosed[i]);
while (prevNode != m_Graph_StartNode)
{
m_VectorPath.push_back(NodeToCoord(prevNode));
prevNode = m_Nodes[prevNode].prevNode;
}
m_VectorPath.push_back(NodeToCoord(prevNode));
// reverse it
std::reverse(m_VectorPath.begin(), m_VectorPath.end());
// push_back
m_MultipleVectorPaths.push_back(m_VectorPath);
}
}
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::CleanUp()
{
m_VectorOrder.clear();
m_VectorPath.clear();
// TODO: if multiple Path, clear all multiple Paths
if (m_Nodes)
delete[] m_Nodes;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::GenerateData()
{
// Build Graph
InitGraph();
- // Calc Shortest Parth
+ // Calc Shortest Path
StartShortestPathSearch();
// Fill Shortest Path
MakeShortestPathVector();
// Make Outputs
MakeOutputs();
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
}
} /* end namespace itk */
#endif // __itkShortestPathImageFilter_txx
diff --git a/Modules/GraphAlgorithms/itkShortestPathNode.h b/Modules/GraphAlgorithms/itkShortestPathNode.h
index 181444788d..07de4b2f78 100644
--- a/Modules/GraphAlgorithms/itkShortestPathNode.h
+++ b/Modules/GraphAlgorithms/itkShortestPathNode.h
@@ -1,37 +1,37 @@
/*============================================================================
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 __itkShortestPathNode_h_
#define __itkShortestPathNode_h_
#include "MitkGraphAlgorithmsExports.h"
namespace itk
{
typedef double DistanceType; // Type to declare the costs
typedef unsigned int
NodeNumType; // Type for Node Numeration: unsignend int for up to 4.2 billion pixel in 32Bit system
class MITKGRAPHALGORITHMS_EXPORT ShortestPathNode
{
public:
DistanceType distance; // minimal costs from StartPoint to this pixel
- DistanceType distAndEst; // Distance+Estimated Distnace to target
+ DistanceType distAndEst; // Distance+Estimated Distance to target
NodeNumType prevNode; // previous node. Important to find the Shortest Path
NodeNumType mainListIndex; // Indexnumber of this node in m_Nodes
bool closed; // determines if this node is closes, so its optimal path to startNode is known
};
// bool operator<(const ShortestPathNode &a) const;
// bool operator==(const ShortestPathNode &a) const;
}
#endif
diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp
index 14922b27b8..83891633bf 100644
--- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp
+++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp
@@ -1,477 +1,477 @@
/*============================================================================
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 "mitkExtractDirectedPlaneImageFilter.h"
#include "mitkAbstractTransformGeometry.h"
//#include "mitkImageMapperGL2D.h"
#include "vtkMitkThickSlicesFilter.h"
#include <mitkDataNode.h>
#include <mitkProperties.h>
#include <mitkResliceMethodProperty.h>
#include <vtkGeneralTransform.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkPoints.h>
#include <vtkSmartPointer.h>
#include <vtkTransform.h>
#include <vtkTransform.h>
mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() : m_WorldGeometry(nullptr)
{
MITK_WARN << "Class ExtractDirectedPlaneImageFilter is deprecated! Use ExtractSliceFilter instead.";
m_Reslicer = vtkImageReslice::New();
m_TargetTimestep = 0;
m_InPlaneResampleExtentByGeometry = true;
m_ResliceInterpolationProperty = nullptr; // VtkResliceInterpolationProperty::New(); //TODO initial with value
m_ThickSlicesMode = 0;
m_ThickSlicesNum = 1;
}
mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter()
{
if (m_ResliceInterpolationProperty != nullptr)
m_ResliceInterpolationProperty->Delete();
m_Reslicer->Delete();
}
void mitk::ExtractDirectedPlaneImageFilter::GenerateData()
{
// A world geometry must be set...
if (m_WorldGeometry == nullptr)
{
itkWarningMacro(<< "No world geometry has been set. Returning.");
return;
}
auto *input = dynamic_cast<ImageToImageFilter::InputImageType *>(this->GetInput());
input->Update();
if (input == nullptr)
{
itkWarningMacro(<< "No input set.");
return;
}
const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry();
if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() == 0))
{
itkWarningMacro(<< "Error reading input image geometry.");
return;
}
// Get the target timestep; if none is set, use the lowest given.
unsigned int timestep = m_TargetTimestep;
if (inputTimeGeometry->IsValidTimeStep(timestep) == false)
{
itkWarningMacro(<< "This is not a valid timestep: " << timestep);
return;
}
// check if there is something to display.
if (!input->IsVolumeSet(timestep))
{
itkWarningMacro(<< "No volume data existent at given timestep " << timestep);
return;
}
Image::RegionType requestedRegion = input->GetLargestPossibleRegion();
requestedRegion.SetIndex(3, timestep);
requestedRegion.SetSize(3, 1);
requestedRegion.SetSize(4, 1);
input->SetRequestedRegion(&requestedRegion);
input->Update();
vtkImageData *inputData = input->GetVtkImageData(timestep);
if (inputData == nullptr)
{
itkWarningMacro(<< "Could not extract vtk image data for given timestep" << timestep);
return;
}
double spacing[3];
inputData->GetSpacing(spacing);
// how big the area is in physical coordinates: widthInMM x heightInMM pixels
mitk::ScalarType widthInMM, heightInMM;
// where we want to sample
Point3D origin;
Vector3D right, bottom, normal;
Vector3D rightInIndex, bottomInIndex;
assert(input->GetTimeGeometry() == inputTimeGeometry);
// take transform of input image into account
BaseGeometry *inputGeometry = inputTimeGeometry->GetGeometryForTimeStep(timestep);
if (inputGeometry == nullptr)
{
itkWarningMacro(<< "There is no Geometry3D at given timestep " << timestep);
return;
}
ScalarType mmPerPixel[2];
// Bounds information for reslicing (only required if reference geometry
// is present)
double bounds[6];
bool boundsInitialized = false;
for (auto &bound : bounds)
{
bound = 0.0;
}
Vector2D extent;
extent.Fill(0.0);
// Do we have a simple PlaneGeometry?
if (dynamic_cast<const PlaneGeometry *>(m_WorldGeometry) != nullptr &&
dynamic_cast<const AbstractTransformGeometry *>(m_WorldGeometry) == nullptr)
{
const auto *planeGeometry = static_cast<const PlaneGeometry *>(m_WorldGeometry);
origin = planeGeometry->GetOrigin();
right = planeGeometry->GetAxisVector(0);
bottom = planeGeometry->GetAxisVector(1);
normal = planeGeometry->GetNormal();
if (m_InPlaneResampleExtentByGeometry)
{
// Resampling grid corresponds to the current world geometry. This
// means that the spacing of the output 2D image depends on the
// currently selected world geometry, and *not* on the image itself.
extent[0] = m_WorldGeometry->GetExtent(0);
extent[1] = m_WorldGeometry->GetExtent(1);
}
else
{
// Resampling grid corresponds to the input geometry. This means that
// the spacing of the output 2D image is directly derived from the
// associated input image, regardless of the currently selected world
// geometry.
inputGeometry->WorldToIndex(right, rightInIndex);
inputGeometry->WorldToIndex(bottom, bottomInIndex);
extent[0] = rightInIndex.GetNorm();
extent[1] = bottomInIndex.GetNorm();
}
// Get the extent of the current world geometry and calculate resampling
// spacing therefrom.
widthInMM = m_WorldGeometry->GetExtentInMM(0);
heightInMM = m_WorldGeometry->GetExtentInMM(1);
mmPerPixel[0] = widthInMM / extent[0];
mmPerPixel[1] = heightInMM / extent[1];
right.Normalize();
bottom.Normalize();
normal.Normalize();
// origin += right * ( mmPerPixel[0] * 0.5 );
// origin += bottom * ( mmPerPixel[1] * 0.5 );
// widthInMM -= mmPerPixel[0];
// heightInMM -= mmPerPixel[1];
// Use inverse transform of the input geometry for reslicing the 3D image
m_Reslicer->SetResliceTransform(inputGeometry->GetVtkTransform()->GetLinearInverse());
// Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D)
m_Reslicer->SetBackgroundLevel(-32768);
// Check if a reference geometry does exist (as would usually be the case for
// PlaneGeometry).
// Note: this is currently not strictly required, but could facilitate
// correct plane clipping.
if (m_WorldGeometry->GetReferenceGeometry())
{
// 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.
boundsInitialized =
this->CalculateClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds);
}
}
// Do we have an AbstractTransformGeometry?
else if (dynamic_cast<const AbstractTransformGeometry *>(m_WorldGeometry))
{
const auto *abstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(m_WorldGeometry);
extent[0] = abstractGeometry->GetParametricExtent(0);
extent[1] = abstractGeometry->GetParametricExtent(1);
widthInMM = abstractGeometry->GetParametricExtentInMM(0);
heightInMM = abstractGeometry->GetParametricExtentInMM(1);
mmPerPixel[0] = widthInMM / extent[0];
mmPerPixel[1] = heightInMM / extent[1];
origin = abstractGeometry->GetPlane()->GetOrigin();
right = abstractGeometry->GetPlane()->GetAxisVector(0);
right.Normalize();
bottom = abstractGeometry->GetPlane()->GetAxisVector(1);
bottom.Normalize();
normal = abstractGeometry->GetPlane()->GetNormal();
normal.Normalize();
// Use a combination of the InputGeometry *and* the possible non-rigid
// AbstractTransformGeometry for reslicing the 3D Image
vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New();
composedResliceTransform->Identity();
composedResliceTransform->Concatenate(inputGeometry->GetVtkTransform()->GetLinearInverse());
composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform());
m_Reslicer->SetResliceTransform(composedResliceTransform);
// Set background level to BLACK instead of translucent, to avoid
// boundary artifacts (see PlaneGeometryDataVtkMapper3D)
m_Reslicer->SetBackgroundLevel(-1023);
composedResliceTransform->Delete();
}
else
{
itkWarningMacro(<< "World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry.");
return;
}
// Make sure that the image to be resliced has a certain minimum size.
if ((extent[0] <= 2) && (extent[1] <= 2))
{
itkWarningMacro(<< "Image is too small to be resliced...");
return;
}
vtkSmartPointer<vtkImageChangeInformation> unitSpacingImageFilter = vtkImageChangeInformation::New();
unitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0);
unitSpacingImageFilter->SetInputData(inputData);
m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort());
// m_Reslicer->SetInput( inputData );
m_Reslicer->SetOutputDimensionality(2);
m_Reslicer->SetOutputOrigin(0.0, 0.0, 0.0);
Vector2D pixelsPerMM;
pixelsPerMM[0] = 1.0 / mmPerPixel[0];
pixelsPerMM[1] = 1.0 / mmPerPixel[1];
- // calulate the originArray and the orientations for the reslice-filter
+ // calculate the originArray and the orientations for the reslice-filter
double originArray[3];
itk2vtk(origin, originArray);
m_Reslicer->SetResliceAxesOrigin(originArray);
double cosines[9];
// direction of the X-axis of the sampled result
vnl2vtk(right.GetVnlVector(), cosines);
// direction of the Y-axis of the sampled result
vnl2vtk(bottom.GetVnlVector(), cosines + 3);
// normal of the plane
vnl2vtk(normal.GetVnlVector(), cosines + 6);
m_Reslicer->SetResliceAxesDirectionCosines(cosines);
int xMin, xMax, yMin, yMax;
if (boundsInitialized)
{
xMin = static_cast<int>(bounds[0] / mmPerPixel[0]); //+ 0.5 );
xMax = static_cast<int>(bounds[1] / mmPerPixel[0]); //+ 0.5 );
yMin = static_cast<int>(bounds[2] / mmPerPixel[1]); //+ 0.5);
yMax = static_cast<int>(bounds[3] / mmPerPixel[1]); //+ 0.5 );
}
else
{
// If no reference geometry is available, we also don't know about the
// maximum plane size; so the overlap is just ignored
xMin = yMin = 0;
xMax = static_cast<int>(extent[0] - pixelsPerMM[0]); //+ 0.5 );
yMax = static_cast<int>(extent[1] - pixelsPerMM[1]); //+ 0.5 );
}
m_Reslicer->SetOutputSpacing(mmPerPixel[0], mmPerPixel[1], 1.0);
// xMax and yMax are meant exclusive until now, whereas
// SetOutputExtent wants an inclusive bound. Thus, we need
// to subtract 1.
m_Reslicer->SetOutputExtent(xMin, xMax - 1, yMin, yMax - 1, 0, 1);
// 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.
m_Reslicer->Modified();
m_Reslicer->ReleaseDataFlagOn();
m_Reslicer->Update();
// 1. Check the result
vtkImageData *reslicedImage = m_Reslicer->GetOutput();
if ((reslicedImage == nullptr) || (reslicedImage->GetDataDimension() < 1))
{
itkWarningMacro(<< "Reslicer returned empty image");
return;
}
unsigned int dimensions[2];
dimensions[0] = (unsigned int)extent[0];
dimensions[1] = (unsigned int)extent[1];
Vector3D spacingVector;
FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0);
mitk::Image::Pointer resultImage = this->GetOutput();
resultImage->Initialize(input->GetPixelType(), 2, dimensions);
resultImage->SetSpacing(spacingVector);
}
void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation()
{
Superclass::GenerateOutputInformation();
}
bool mitk::ExtractDirectedPlaneImageFilter::CalculateClippedPlaneBounds(const BaseGeometry *boundingGeometry,
const PlaneGeometry *planeGeometry,
double *bounds)
{
// Clip the plane with the bounding geometry. To do so, the corner points
// of the bounding box are transformed by the inverse transformation
// matrix, and the transformed bounding box edges derived therefrom are
// clipped with the plane z=0. The resulting min/max values are taken as
// bounds for the image reslicer.
const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox();
BoundingBox::PointType bbMin = boundingBox->GetMinimum();
BoundingBox::PointType bbMax = boundingBox->GetMaximum();
vtkPoints *points = vtkPoints::New();
if (boundingGeometry->GetImageGeometry())
{
points->InsertPoint(0, bbMin[0] - 0.5, bbMin[1] - 0.5, bbMin[2] - 0.5);
points->InsertPoint(1, bbMin[0] - 0.5, bbMin[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(2, bbMin[0] - 0.5, bbMax[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(3, bbMin[0] - 0.5, bbMax[1] - 0.5, bbMin[2] - 0.5);
points->InsertPoint(4, bbMax[0] - 0.5, bbMin[1] - 0.5, bbMin[2] - 0.5);
points->InsertPoint(5, bbMax[0] - 0.5, bbMin[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(6, bbMax[0] - 0.5, bbMax[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(7, bbMax[0] - 0.5, bbMax[1] - 0.5, bbMin[2] - 0.5);
}
else
{
points->InsertPoint(0, bbMin[0], bbMin[1], bbMin[2]);
points->InsertPoint(1, bbMin[0], bbMin[1], bbMax[2]);
points->InsertPoint(2, bbMin[0], bbMax[1], bbMax[2]);
points->InsertPoint(3, bbMin[0], bbMax[1], bbMin[2]);
points->InsertPoint(4, bbMax[0], bbMin[1], bbMin[2]);
points->InsertPoint(5, bbMax[0], bbMin[1], bbMax[2]);
points->InsertPoint(6, bbMax[0], bbMax[1], bbMax[2]);
points->InsertPoint(7, bbMax[0], bbMax[1], bbMin[2]);
}
vtkPoints *newPoints = vtkPoints::New();
vtkTransform *transform = vtkTransform::New();
transform->Identity();
transform->Concatenate(planeGeometry->GetVtkTransform()->GetLinearInverse());
transform->Concatenate(boundingGeometry->GetVtkTransform());
transform->TransformPoints(points, newPoints);
transform->Delete();
bounds[0] = bounds[2] = 10000000.0;
bounds[1] = bounds[3] = -10000000.0;
bounds[4] = bounds[5] = 0.0;
this->LineIntersectZero(newPoints, 0, 1, bounds);
this->LineIntersectZero(newPoints, 1, 2, bounds);
this->LineIntersectZero(newPoints, 2, 3, bounds);
this->LineIntersectZero(newPoints, 3, 0, bounds);
this->LineIntersectZero(newPoints, 0, 4, bounds);
this->LineIntersectZero(newPoints, 1, 5, bounds);
this->LineIntersectZero(newPoints, 2, 6, bounds);
this->LineIntersectZero(newPoints, 3, 7, bounds);
this->LineIntersectZero(newPoints, 4, 5, bounds);
this->LineIntersectZero(newPoints, 5, 6, bounds);
this->LineIntersectZero(newPoints, 6, 7, bounds);
this->LineIntersectZero(newPoints, 7, 4, bounds);
// clean up vtk data
points->Delete();
newPoints->Delete();
if ((bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0))
{
return false;
}
else
{
// The resulting bounds must be adjusted by the plane spacing, since we
// we have so far dealt with index coordinates
const mitk::Vector3D planeSpacing = planeGeometry->GetSpacing();
bounds[0] *= planeSpacing[0];
bounds[1] *= planeSpacing[0];
bounds[2] *= planeSpacing[1];
bounds[3] *= planeSpacing[1];
bounds[4] *= planeSpacing[2];
bounds[5] *= planeSpacing[2];
return true;
}
}
bool mitk::ExtractDirectedPlaneImageFilter::LineIntersectZero(vtkPoints *points, int p1, int p2, double *bounds)
{
double point1[3];
double point2[3];
points->GetPoint(p1, point1);
points->GetPoint(p2, point2);
if ((point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]))
{
double x, y;
x = (point1[0] * point2[2] - point1[2] * point2[0]) / (point2[2] - point1[2]);
y = (point1[1] * point2[2] - point1[2] * point2[1]) / (point2[2] - point1[2]);
if (x < bounds[0])
{
bounds[0] = x;
}
if (x > bounds[1])
{
bounds[1] = x;
}
if (y < bounds[2])
{
bounds[2] = y;
}
if (y > bounds[3])
{
bounds[3] = y;
}
bounds[4] = bounds[5] = 0.0;
return true;
}
return false;
}
diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp
index 6070306cfa..5d291e1da2 100644
--- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp
+++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp
@@ -1,284 +1,284 @@
/*============================================================================
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 "mitkExtractDirectedPlaneImageFilterNew.h"
#include "itkImageRegionIterator.h"
#include "mitkImageCast.h"
#include "mitkImageTimeSelector.h"
#include <mitkImageAccessByItk.h>
mitk::ExtractDirectedPlaneImageFilterNew::ExtractDirectedPlaneImageFilterNew()
: m_CurrentWorldPlaneGeometry(nullptr), m_ImageGeometry(nullptr), m_ActualInputTimestep(0)
{
MITK_WARN << "Class ExtractDirectedPlaneImageFilterNew is deprecated! Use ExtractSliceFilter instead.";
}
mitk::ExtractDirectedPlaneImageFilterNew::~ExtractDirectedPlaneImageFilterNew()
{
}
void mitk::ExtractDirectedPlaneImageFilterNew::GenerateData()
{
mitk::Image::ConstPointer inputImage = ImageToImageFilter::GetInput(0);
if (!inputImage)
{
MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!" << std::endl;
itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!");
return;
}
m_ImageGeometry = inputImage->GetGeometry();
// If no timestep is set, the lowest given will be selected
// const mitk::TimeGeometry* inputTimeGeometry = this->GetInput()->GetTimeGeometry();
if (inputImage->GetDimension() > 4 || inputImage->GetDimension() < 2)
{
MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew:GenerateData works only with 3D and 3D+t images, sorry."
<< std::endl;
itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew works only with 3D and 3D+t images, sorry.");
return;
}
else if (inputImage->GetDimension() == 4)
{
mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New();
timeselector->SetInput(inputImage);
timeselector->SetTimeNr(m_ActualInputTimestep);
timeselector->UpdateLargestPossibleRegion();
inputImage = timeselector->GetOutput();
}
else if (inputImage->GetDimension() == 2)
{
mitk::Image::Pointer resultImage = ImageToImageFilter::GetOutput();
resultImage = const_cast<mitk::Image *>(inputImage.GetPointer());
ImageToImageFilter::SetNthOutput(0, resultImage);
return;
}
if (!m_CurrentWorldPlaneGeometry)
{
MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew::GenerateData has no CurrentWorldPlaneGeometry set"
<< std::endl;
return;
}
AccessFixedDimensionByItk(inputImage, ItkSliceExtraction, 3);
} // Generate Data
void mitk::ExtractDirectedPlaneImageFilterNew::GenerateOutputInformation()
{
Superclass::GenerateOutputInformation();
}
/*
* The desired slice is extracted by filling the image`s corresponding pixel values in an empty 2 dimensional itk::Image
* Therefor the itk image`s extent in pixel (in each direction) is doubled and its spacing (also in each direction) is
* divided by two
* (similar to the shannon theorem).
*/
template <typename TPixel, unsigned int VImageDimension>
void mitk::ExtractDirectedPlaneImageFilterNew::ItkSliceExtraction(const itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::Image<TPixel, VImageDimension - 1> SliceImageType;
typedef itk::ImageRegionConstIterator<SliceImageType> SliceIterator;
// Creating an itk::Image that represents the sampled slice
typename SliceImageType::Pointer resultSlice = SliceImageType::New();
typename SliceImageType::IndexType start;
start[0] = 0;
start[1] = 0;
Point3D origin = m_CurrentWorldPlaneGeometry->GetOrigin();
Vector3D right = m_CurrentWorldPlaneGeometry->GetAxisVector(0);
Vector3D bottom = m_CurrentWorldPlaneGeometry->GetAxisVector(1);
// Calculation the sample-spacing, i.e the half of the smallest spacing existing in the original image
Vector3D newPixelSpacing = m_ImageGeometry->GetSpacing();
float minSpacing = newPixelSpacing[0];
for (unsigned int i = 1; i < newPixelSpacing.Size(); i++)
{
if (newPixelSpacing[i] < minSpacing)
{
minSpacing = newPixelSpacing[i];
}
}
newPixelSpacing[0] = 0.5 * minSpacing;
newPixelSpacing[1] = 0.5 * minSpacing;
newPixelSpacing[2] = 0.5 * minSpacing;
float pixelSpacing[2];
pixelSpacing[0] = newPixelSpacing[0];
pixelSpacing[1] = newPixelSpacing[1];
// Calculating the size of the sampled slice
typename SliceImageType::SizeType size;
Vector2D extentInMM;
extentInMM[0] = m_CurrentWorldPlaneGeometry->GetExtentInMM(0);
extentInMM[1] = m_CurrentWorldPlaneGeometry->GetExtentInMM(1);
- // The maximum extent is the lenght of the diagonal of the considered plane
+ // The maximum extent is the length of the diagonal of the considered plane
double maxExtent = sqrt(extentInMM[0] * extentInMM[0] + extentInMM[1] * extentInMM[1]);
unsigned int xTranlation = (maxExtent - extentInMM[0]);
unsigned int yTranlation = (maxExtent - extentInMM[1]);
size[0] = (maxExtent + xTranlation) / newPixelSpacing[0];
size[1] = (maxExtent + yTranlation) / newPixelSpacing[1];
// Creating an ImageRegion Object
typename SliceImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
// Defining the image`s extent and origin by passing the region to it and allocating memory for it
resultSlice->SetRegions(region);
resultSlice->SetSpacing(pixelSpacing);
resultSlice->Allocate();
/*
* Here we create an new geometry so that the transformations are calculated correctly (our resulting slice has a
* different bounding box and spacing)
* The original current worldgeometry must be cloned because we have to keep the directions of the axis vector which
* represents the rotation
*/
right.Normalize();
bottom.Normalize();
// Here we translate the origin to adapt the new geometry to the previous calculated extent
origin[0] -= xTranlation * right[0] + yTranlation * bottom[0];
origin[1] -= xTranlation * right[1] + yTranlation * bottom[1];
origin[2] -= xTranlation * right[2] + yTranlation * bottom[2];
// Putting it together for the new geometry
mitk::BaseGeometry::Pointer newSliceGeometryTest =
dynamic_cast<BaseGeometry *>(m_CurrentWorldPlaneGeometry->Clone().GetPointer());
newSliceGeometryTest->ChangeImageGeometryConsideringOriginOffset(true);
// Workaround because of BUG (#6505)
newSliceGeometryTest->GetIndexToWorldTransform()->SetMatrix(
m_CurrentWorldPlaneGeometry->GetIndexToWorldTransform()->GetMatrix());
// Workaround end
newSliceGeometryTest->SetOrigin(origin);
ScalarType bounds[6] = {0, static_cast<ScalarType>(size[0]), 0, static_cast<ScalarType>(size[1]), 0, 1};
newSliceGeometryTest->SetBounds(bounds);
newSliceGeometryTest->SetSpacing(newPixelSpacing);
newSliceGeometryTest->Modified();
// Workaround because of BUG (#6505)
itk::MatrixOffsetTransformBase<mitk::ScalarType, 3, 3>::MatrixType tempTransform =
newSliceGeometryTest->GetIndexToWorldTransform()->GetMatrix();
// Workaround end
/*
* Now we iterate over the recently created slice.
* For each slice - pixel we check whether there is an according
* pixel in the input - image which can be set in the slice.
* In this way a slice is sampled out of the input - image regrading to the given PlaneGeometry
*/
Point3D currentSliceIndexPointIn2D;
Point3D currentImageWorldPointIn3D;
typename InputImageType::IndexType inputIndex;
SliceIterator sliceIterator(resultSlice, resultSlice->GetLargestPossibleRegion());
sliceIterator.GoToBegin();
while (!sliceIterator.IsAtEnd())
{
/*
* Here we add 0.5 to to assure that the indices are correctly transformed.
* (Because of the 0.5er Bug)
*/
currentSliceIndexPointIn2D[0] = sliceIterator.GetIndex()[0] + 0.5;
currentSliceIndexPointIn2D[1] = sliceIterator.GetIndex()[1] + 0.5;
currentSliceIndexPointIn2D[2] = 0;
newSliceGeometryTest->IndexToWorld(currentSliceIndexPointIn2D, currentImageWorldPointIn3D);
m_ImageGeometry->WorldToIndex(currentImageWorldPointIn3D, inputIndex);
if (m_ImageGeometry->IsIndexInside(inputIndex))
{
resultSlice->SetPixel(sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex));
}
else
{
resultSlice->SetPixel(sliceIterator.GetIndex(), 0);
}
++sliceIterator;
}
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
GrabItkImageMemory(resultSlice, resultImage, nullptr, false);
resultImage->SetClonedGeometry(newSliceGeometryTest);
// Workaround because of BUG (#6505)
resultImage->GetGeometry()->GetIndexToWorldTransform()->SetMatrix(tempTransform);
// Workaround end
}
///**TEST** May ba a little bit more efficient but doesn`t already work/
// right.Normalize();
// bottom.Normalize();
// Point3D currentImagePointIn3D = origin /*+ bottom*newPixelSpacing*/;
// unsigned int columns ( 0 );
/**ENDE**/
/****TEST***/
// SliceImageType::IndexType index = sliceIterator.GetIndex();
// if ( columns == (extentInPixel[0]) )
//{
// If we are at the end of a row, then we have to go to the beginning of the next row
// currentImagePointIn3D = origin;
// currentImagePointIn3D += newPixelSpacing[1]*bottom*index[1];
// columns = 0;
// m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex);
//}
// else
//{
////
// if ( columns != 0 )
//{
// currentImagePointIn3D += newPixelSpacing[0]*right;
//}
// m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex);
//}
// if ( m_ImageGeometry->IsIndexInside( inputIndex ))
//{
// resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) );
//}
// else if (currentImagePointIn3D == origin)
//{
// Point3D temp;
// temp[0] = bottom[0]*newPixelSpacing[0]*0.5;
// temp[1] = bottom[1]*newPixelSpacing[1]*0.5;
// temp[2] = bottom[2]*newPixelSpacing[2]*0.5;
// origin[0] += temp[0];
// origin[1] += temp[1];
// origin[2] += temp[2];
// currentImagePointIn3D = origin;
// m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex);
// if ( m_ImageGeometry->IsIndexInside( inputIndex ))
//{
// resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) );
//}
//}
/****TEST ENDE****/
diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp
index 2a754f0045..19d3bbf43d 100644
--- a/Modules/ImageExtraction/mitkExtractImageFilter.cpp
+++ b/Modules/ImageExtraction/mitkExtractImageFilter.cpp
@@ -1,259 +1,259 @@
/*============================================================================
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 "mitkExtractImageFilter.h"
#include "mitkITKImageImport.h"
#include "mitkImageCast.h"
#include "mitkImageTimeSelector.h"
#include "mitkPlaneGeometry.h"
#include <itkExtractImageFilter.h>
#include <mitkImageAccessByItk.h>
mitk::ExtractImageFilter::ExtractImageFilter()
: m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_DirectionCollapseToStrategy(DIRECTIONCOLLAPSETOGUESS)
{
MITK_WARN << "Class ExtractImageFilter is deprecated! Use ExtractSliceFilter instead.";
}
mitk::ExtractImageFilter::~ExtractImageFilter()
{
}
void mitk::ExtractImageFilter::GenerateData()
{
Image::ConstPointer input = ImageToImageFilter::GetInput(0);
if ((input->GetDimension() > 4) || (input->GetDimension() < 2))
{
MITK_ERROR << "mitk::ExtractImageFilter:GenerateData works only with 3D and 3D+t images, sorry." << std::endl;
itkExceptionMacro("mitk::ExtractImageFilter works only with 3D and 3D+t images, sorry.");
return;
}
else if (input->GetDimension() == 4)
{
mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(input);
timeSelector->SetTimeNr(m_TimeStep);
timeSelector->UpdateLargestPossibleRegion();
input = timeSelector->GetOutput();
}
else if (input->GetDimension() == 2)
{
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
resultImage = const_cast<Image *>(input.GetPointer());
ImageToImageFilter::SetNthOutput(0, resultImage);
return;
}
if (m_SliceDimension >= input->GetDimension())
{
MITK_ERROR << "mitk::ExtractImageFilter:GenerateData m_SliceDimension == " << m_SliceDimension
<< " makes no sense with an " << input->GetDimension() << "D image." << std::endl;
itkExceptionMacro("This is not a sensible value for m_SliceDimension.");
return;
}
AccessFixedDimensionByItk(input, ItkImageProcessing, 3);
// set a nice geometry for display and point transformations
BaseGeometry *inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry();
if (!inputImageGeometry)
{
MITK_ERROR << "In ExtractImageFilter::ItkImageProcessing: Input image has no geometry!" << std::endl;
return;
}
AnatomicalPlane orientation = AnatomicalPlane::Axial;
switch (m_SliceDimension)
{
default:
case 2:
orientation = AnatomicalPlane::Axial;
break;
case 1:
orientation = AnatomicalPlane::Coronal;
break;
case 0:
orientation = AnatomicalPlane::Sagittal;
break;
}
PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New();
planeGeometry->InitializeStandardPlane(inputImageGeometry, orientation, (ScalarType)m_SliceIndex, true, false);
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
planeGeometry->ChangeImageGeometryConsideringOriginOffset(true);
resultImage->SetGeometry(planeGeometry);
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::ExtractImageFilter::ItkImageProcessing(const itk::Image<TPixel, VImageDimension> *itkImage)
{
// use the itk::ExtractImageFilter to get a 2D image
typedef itk::Image<TPixel, VImageDimension> ImageType3D;
typedef itk::Image<TPixel, VImageDimension - 1> ImageType2D;
typedef itk::ExtractImageFilter<ImageType3D, ImageType2D> ExtractImageFilterType;
typename ImageType3D::RegionType inSliceRegion = itkImage->GetLargestPossibleRegion();
inSliceRegion.SetSize(m_SliceDimension, 0);
typename ExtractImageFilterType::Pointer sliceExtractor = ExtractImageFilterType::New();
typename ExtractImageFilterType::DirectionCollapseStrategyEnum collapseStrategy;
switch (m_DirectionCollapseToStrategy)
{
case DIRECTIONCOLLAPSETOUNKOWN:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOUNKOWN;
break;
case DIRECTIONCOLLAPSETOIDENTITY:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOIDENTITY;
break;
case DIRECTIONCOLLAPSETOSUBMATRIX:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOSUBMATRIX;
break;
case DIRECTIONCOLLAPSETOGUESS:
default:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOGUESS;
break;
}
sliceExtractor->SetDirectionCollapseToStrategy(collapseStrategy);
sliceExtractor->SetInput(itkImage);
inSliceRegion.SetIndex(m_SliceDimension, m_SliceIndex);
sliceExtractor->SetExtractionRegion(inSliceRegion);
// calculate the output
sliceExtractor->UpdateLargestPossibleRegion();
typename ImageType2D::Pointer slice = sliceExtractor->GetOutput();
// re-import to MITK
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
GrabItkImageMemory(slice, resultImage, nullptr, false);
}
/*
* What is the input requested region that is required to produce the output
* requested region? By default, the largest possible region is always
* required but this is overridden in many subclasses. For instance, for an
* image processing filter where an output pixel is a simple function of an
* input pixel, the input requested region will be set to the output
* requested region. For an image processing filter where an output pixel is
* a function of the pixels in a neighborhood of an input pixel, then the
* input requested region will need to be larger than the output requested
* region (to avoid introducing artificial boundary conditions). This
* function should never request an input region that is outside the the
* input largest possible region (i.e. implementations of this method should
* crop the input requested region at the boundaries of the input largest
* possible region).
*/
void mitk::ExtractImageFilter::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
ImageToImageFilter::InputImagePointer input = dynamic_cast<ImageToImageFilter::InputImageType *>(this->GetInput());
Image::Pointer output = this->GetOutput();
if (input->GetDimension() == 2)
{
input->SetRequestedRegionToLargestPossibleRegion();
return;
}
Image::RegionType requestedRegion;
requestedRegion = output->GetRequestedRegion();
requestedRegion.SetIndex(0, 0);
requestedRegion.SetIndex(1, 0);
requestedRegion.SetIndex(2, 0);
requestedRegion.SetSize(0, input->GetDimension(0));
requestedRegion.SetSize(1, input->GetDimension(1));
requestedRegion.SetSize(2, input->GetDimension(2));
requestedRegion.SetIndex(m_SliceDimension, m_SliceIndex); // only one slice needed
requestedRegion.SetSize(m_SliceDimension, 1);
input->SetRequestedRegion(&requestedRegion);
}
/*
- * Generate the information decribing the output data. The default
+ * Generate the information describing the output data. The default
* implementation of this method will copy information from the input to the
* output. A filter may override this method if its output will have different
* information than its input. For instance, a filter that shrinks an image will
* need to provide an implementation for this method that changes the spacing of
* the pixels. Such filters should call their superclass' implementation of this
* method prior to changing the information values they need (i.e.
* GenerateOutputInformation() should call
* Superclass::GenerateOutputInformation() prior to changing the information.
*/
void mitk::ExtractImageFilter::GenerateOutputInformation()
{
Image::Pointer output = this->GetOutput();
Image::ConstPointer input = this->GetInput();
if (input.IsNull())
return;
if (m_SliceDimension >= input->GetDimension() && input->GetDimension() != 2)
{
MITK_ERROR << "mitk::ExtractImageFilter:GenerateOutputInformation m_SliceDimension == " << m_SliceDimension
<< " makes no sense with an " << input->GetDimension() << "D image." << std::endl;
itkExceptionMacro("This is not a sensible value for m_SliceDimension.");
return;
}
unsigned int sliceDimension(m_SliceDimension);
if (input->GetDimension() == 2)
{
sliceDimension = 2;
}
unsigned int tmpDimensions[2];
switch (sliceDimension)
{
default:
case 2:
// orientation = AnatomicalPlane::Axial;
tmpDimensions[0] = input->GetDimension(0);
tmpDimensions[1] = input->GetDimension(1);
break;
case 1:
// orientation = AnatomicalPlane::Coronal;
tmpDimensions[0] = input->GetDimension(0);
tmpDimensions[1] = input->GetDimension(2);
break;
case 0:
// orientation = AnatomicalPlane::Sagittal;
tmpDimensions[0] = input->GetDimension(1);
tmpDimensions[1] = input->GetDimension(2);
break;
}
output->Initialize(input->GetPixelType(), 2, tmpDimensions, 1 /*input->GetNumberOfChannels()*/);
// initialize the spacing of the output
/*
Vector3D spacing = input->GetSlicedGeometry()->GetSpacing();
if(input->GetDimension()>=2)
spacing[2]=spacing[1];
else
spacing[2] = 1.0;
output->GetSlicedGeometry()->SetSpacing(spacing);
*/
output->SetPropertyList(input->GetPropertyList()->Clone());
}
diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp
index 3114c2b52c..33f285ece8 100644
--- a/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp
@@ -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.
============================================================================*/
#include "mitkImageStatisticsCalculator.h"
#include "itkMultiGaussianImageSource.h"
#include "mitkTestingMacros.h"
#include <mitkITKImageImport.h>
#include <itkImageRegionIterator.h>
#include <stdexcept>
#include <itkDOMNode.h>
#include <itkDOMReader.h>
#include <mitkHotspotMaskGenerator.h>
#include <mitkImageMaskGenerator.h>
#include <mitkIOUtil.h>
/**
\section hotspotCalculationTestCases Testcases
To see the different Hotspot-Testcases have a look at the \ref hotspottestdoc.
Note from an intensive session of checking the test results:
- itk::MultiGaussianImageSource needs a review
- the test idea is ok, but the combination of XML files for parameters and MultiGaussianImageSource has serious flaws
- the XML file should contain exactly the parameters that MultiGaussianImageSource requires
- in contrast, now the XML file mentions index coordinates for gaussian centers while the MultiGaussianImageSource expects world coordinates
- this requires a transformation (index * spacing assuming no rotation) that was actually broken until recently
*/
struct mitkImageStatisticsHotspotTestClass
{
/**
\brief Test parameters for one test case.
Describes all aspects of a single test case:
- parameters to generate a test image
- parameters of a ROI that describes where to calculate statistics
- expected statistics results
*/
struct Parameters
{
public:
// XML-Tag <testimage>
/** \brief XML-Tag "image-rows": size of x-dimension */
int m_ImageRows;
/** \brief XML-Tag "image-columns": size of y-dimension */
int m_ImageColumns;
/** \brief XML-Tag "image-slices": size of z-dimension */
int m_ImageSlices;
/** \brief XML-Tag "numberOfGaussians": number of used gauss-functions */
int m_NumberOfGaussian;
/** \brief XML-Tags "spacingX", "spacingY", "spacingZ": spacing of image in every direction */
double m_Spacing[3];
/** \brief XML-Tag "entireHotSpotInImage" */
unsigned int m_EntireHotspotInImage;
// XML-Tag <gaussian>
/**
\brief XML-Tag "centerIndexX: gaussian parameter
\warning This parameter READS the centerIndexX parameter from file and is THEN MISUSED to calculate some position in world coordinates, so we require double.
*/
std::vector<double> m_CenterX;
/** \brief XML-Tag "centerIndexY: gaussian parameter
\warning This parameter READS the centerIndexX parameter from file and is THEN MISUSED to calculate some position in world coordinates, so we require double.
*/
std::vector<double> m_CenterY;
/** \brief XML-Tag "centerIndexZ: gaussian parameter
\warning This parameter READS the centerIndexX parameter from file and is THEN MISUSED to calculate some position in world coordinates, so we require double.
*/
std::vector<double> m_CenterZ;
/** \brief XML-Tag "deviationX: gaussian parameter */
std::vector<double> m_SigmaX;
/** \brief XML-Tag "deviationY: gaussian parameter */
std::vector<double> m_SigmaY;
/** \brief XML-Tag "deviationZ: gaussian parameter */
std::vector<double> m_SigmaZ;
/** \brief XML-Tag "altitude: gaussian parameter */
std::vector<double> m_Altitude;
// XML-Tag <segmentation>
/** \brief XML-Tag "numberOfLabels": number of different labels which appear in the mask */
unsigned int m_NumberOfLabels;
/** \brief XML-Tag "hotspotRadiusInMM": radius of hotspot */
double m_HotspotRadiusInMM;
// XML-Tag <roi>
/** \brief XML-Tag "maximumSizeX": maximum position of ROI in x-dimension */
vnl_vector<int> m_MaxIndexX;
/** \brief XML-Tag "minimumSizeX": minimum position of ROI in x-dimension */
vnl_vector<int> m_MinIndexX;
/** \brief XML-Tag "maximumSizeX": maximum position of ROI in y-dimension */
vnl_vector<int> m_MaxIndexY;
/** \brief XML-Tag "minimumSizeX": minimum position of ROI in y-dimension */
vnl_vector<int> m_MinIndexY;
/** \brief XML-Tag "maximumSizeX": maximum position of ROI in z-dimension */
vnl_vector<int> m_MaxIndexZ;
/** \brief XML-Tag "minimumSizeX": minimum position of ROI in z-dimension */
vnl_vector<int> m_MinIndexZ;
/** \brief XML-Tag "label": value of label */
vnl_vector<unsigned int> m_Label;
//XML-Tag <statistic>
/** \brief XML-Tag "minimum": minimum inside hotspot */
vnl_vector<double> m_HotspotMin;
/** \brief XML-Tag "maximum": maximum inside hotspot */
vnl_vector<double> m_HotspotMax;
/** \brief XML-Tag "mean": mean value of hotspot */
vnl_vector<double> m_HotspotMean;
/** \brief XML-Tag "maximumIndexX": x-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMaxIndexX;
/** \brief XML-Tag "maximumIndexX": y-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMaxIndexY;
/** \brief XML-Tag "maximumIndexX": z-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMaxIndexZ;
/** \brief XML-Tag "maximumIndexX": x-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMinIndexX;
/** \brief XML-Tag "maximumIndexX": y-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMinIndexY;
/** \brief XML-Tag "maximumIndexX": z-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMinIndexZ;
/** \brief XML-Tag "maximumIndexX": x-coordinate of hotspot-location */
vnl_vector<int> m_HotspotIndexX;
/** \brief XML-Tag "maximumIndexX": y-coordinate of hotspot-location */
vnl_vector<int> m_HotspotIndexY;
/** \brief XML-Tag "maximumIndexX": z-coordinate of hotspot-location */
vnl_vector<int> m_HotspotIndexZ;
};
/**
\brief Find/Convert integer attribute in itk::DOMNode.
*/
static int GetIntegerAttribute(itk::DOMNode* domNode, const std::string& tag)
{
assert(domNode);
MITK_TEST_CONDITION_REQUIRED( domNode->HasAttribute(tag), "Tag '" << tag << "' is defined in test parameters" );
std::string attributeValue = domNode->GetAttribute(tag);
int resultValue;
try
{
//MITK_TEST_OUTPUT( << "Converting tag value '" << attributeValue << "' for tag '" << tag << "' to integer");
std::stringstream(attributeValue) >> resultValue;
return resultValue;
}
catch(std::exception& /*e*/)
{
MITK_TEST_CONDITION_REQUIRED(false, "Convert tag value '" << attributeValue << "' for tag '" << tag << "' to integer");
return 0; // just to satisfy compiler
}
}
/**
\brief Find/Convert double attribute in itk::DOMNode.
*/
static double GetDoubleAttribute(itk::DOMNode* domNode, const std::string& tag)
{
assert(domNode);
MITK_TEST_CONDITION_REQUIRED( domNode->HasAttribute(tag), "Tag '" << tag << "' is defined in test parameters" );
std::string attributeValue = domNode->GetAttribute(tag);
double resultValue;
try
{
//MITK_TEST_OUTPUT( << "Converting tag value '" << attributeValue << "' for tag '" << tag << "' to double");
std::stringstream(attributeValue) >> resultValue;
return resultValue;
}
catch(std::exception& /*e*/)
{
MITK_TEST_CONDITION_REQUIRED(false, "Convert tag value '" << attributeValue << "' for tag '" << tag << "' to double");
return 0.0; // just to satisfy compiler
}
}
/**
\brief Read XML file describing the test parameters.
Reads XML file given in first commandline parameter in order
to construct a Parameters structure. The XML file should be
- structurs as the following example, i.e. we describe the
+ structured as the following example, i.e. we describe the
three test aspects of Parameters in four different tags,
with all the details described as tag attributes. */
/**
\verbatim
<testcase>
<!--
Test case: multi-label mask
-->
<testimage image-rows="50" image-columns="50" image-slices="20" numberOfGaussians="2" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
<gaussian centerIndexX="10" centerIndexY="10" centerIndexZ="10" deviationX="5" deviationY="5" deviationZ="5" altitude="200"/>
<gaussian centerIndexX="40" centerIndexY="40" centerIndexZ="10" deviationX="2" deviationY="4" deviationZ="6" altitude="180"/>
</testimage>
<segmentation numberOfLabels="2" hotspotRadiusInMM="6.2035">
<roi label="1" maximumSizeX="20" minimumSizeX="0" maximumSizeY="20" minimumSizeY="0" maximumSizeZ="20" minimumSizeZ="0"/>
<roi label="2" maximumSizeX="50" minimumSizeX="30" maximumSizeY="50" minimumSizeY="30" maximumSizeZ="20" minimumSizeZ="0"/>
</segmentation>
<statistic hotspotIndexX="10" hotspotIndexY="10" hotspotIndexZ="10" mean="122.053" maximumIndexX="10" maximumIndexY="10" maximumIndexZ="10" maximum="200" minimumIndexX="9" minimumIndexY="9" minimumIndexZ="4" minimum="93.5333"/>
<statistic hotspotIndexX="40" hotspotIndexY="40" hotspotIndexZ="10" mean="61.1749" maximumIndexX="40" maximumIndexY="40" maximumIndexZ="10" maximum="180" minimumIndexX="46" minimumIndexY="39" minimumIndexZ="9" minimum="1.91137"/>
</testcase>
\endverbatim
*/
static Parameters ParseParameters(int argc, char* argv[])
{
MITK_TEST_CONDITION_REQUIRED(argc == 2, "Test is invoked with exactly 1 parameter (XML parameters file)");
MITK_INFO << "Reading parameters from file '" << argv[1] << "'";
std::string filename = argv[1];
Parameters result;
itk::DOMNodeXMLReader::Pointer xmlReader = itk::DOMNodeXMLReader::New();
xmlReader->SetFileName( filename );
try
{
xmlReader->Update();
itk::DOMNode::Pointer domRoot = xmlReader->GetOutput();
typedef std::vector<itk::DOMNode*> NodeList;
NodeList testimages;
domRoot->GetChildren("testimage", testimages);
MITK_TEST_CONDITION_REQUIRED( testimages.size() == 1, "One test image defined" )
itk::DOMNode* testimage = testimages[0];
result.m_ImageRows = GetIntegerAttribute( testimage, "image-rows" );
result.m_ImageColumns = GetIntegerAttribute( testimage, "image-columns" );
result.m_ImageSlices = GetIntegerAttribute( testimage, "image-slices" );
result.m_NumberOfGaussian = GetIntegerAttribute( testimage, "numberOfGaussians" );
result.m_Spacing[0] = GetDoubleAttribute(testimage, "spacingX");
result.m_Spacing[1] = GetDoubleAttribute(testimage, "spacingY");
result.m_Spacing[2] = GetDoubleAttribute(testimage, "spacingZ");
result.m_EntireHotspotInImage = GetIntegerAttribute( testimage, "entireHotSpotInImage" );
MITK_TEST_OUTPUT( << "Read size parameters (x,y,z): " << result.m_ImageRows << "," << result.m_ImageColumns << "," << result.m_ImageSlices);
MITK_TEST_OUTPUT( << "Read spacing parameters (x,y,z): " << result.m_Spacing[0] << "," << result.m_Spacing[1] << "," << result.m_Spacing[2]);
NodeList gaussians;
testimage->GetChildren("gaussian", gaussians);
MITK_TEST_CONDITION_REQUIRED( gaussians.size() >= 1, "At least one gaussian is defined" )
result.m_CenterX.resize(result.m_NumberOfGaussian);
result.m_CenterY.resize(result.m_NumberOfGaussian);
result.m_CenterZ.resize(result.m_NumberOfGaussian);
result.m_SigmaX.resize(result.m_NumberOfGaussian);
result.m_SigmaY.resize(result.m_NumberOfGaussian);
result.m_SigmaZ.resize(result.m_NumberOfGaussian);
result.m_Altitude.resize(result.m_NumberOfGaussian);
for(int i = 0; i < result.m_NumberOfGaussian ; ++i)
{
itk::DOMNode* gaussian = gaussians[i];
result.m_CenterX[i] = GetIntegerAttribute(gaussian, "centerIndexX");
result.m_CenterY[i] = GetIntegerAttribute(gaussian, "centerIndexY");
result.m_CenterZ[i] = GetIntegerAttribute(gaussian, "centerIndexZ");
result.m_SigmaX[i] = GetDoubleAttribute(gaussian, "deviationX");
result.m_SigmaY[i] = GetDoubleAttribute(gaussian, "deviationY");
result.m_SigmaZ[i] = GetDoubleAttribute(gaussian, "deviationZ");
result.m_Altitude[i] = GetDoubleAttribute(gaussian, "altitude");
result.m_CenterX[i] = result.m_CenterX[i] * result.m_Spacing[0];
result.m_CenterY[i] = result.m_CenterY[i] * result.m_Spacing[1];
result.m_CenterZ[i] = result.m_CenterZ[i] * result.m_Spacing[2];
result.m_SigmaX[i] = result.m_SigmaX[i] * result.m_Spacing[0];
result.m_SigmaY[i] = result.m_SigmaY[i] * result.m_Spacing[1];
result.m_SigmaZ[i] = result.m_SigmaZ[i] * result.m_Spacing[2];
}
NodeList segmentations;
domRoot->GetChildren("segmentation", segmentations);
MITK_TEST_CONDITION_REQUIRED( segmentations.size() == 1, "One segmentation defined");
itk::DOMNode* segmentation = segmentations[0];
result.m_NumberOfLabels = GetIntegerAttribute(segmentation, "numberOfLabels");
result.m_HotspotRadiusInMM = GetDoubleAttribute(segmentation, "hotspotRadiusInMM");
// read ROI parameters, fill result structure
NodeList rois;
segmentation->GetChildren("roi", rois);
MITK_TEST_CONDITION_REQUIRED( rois.size() >= 1, "At least one ROI defined" )
result.m_MaxIndexX.set_size(result.m_NumberOfLabels);
result.m_MinIndexX.set_size(result.m_NumberOfLabels);
result.m_MaxIndexY.set_size(result.m_NumberOfLabels);
result.m_MinIndexY.set_size(result.m_NumberOfLabels);
result.m_MaxIndexZ.set_size(result.m_NumberOfLabels);
result.m_MinIndexZ.set_size(result.m_NumberOfLabels);
result.m_Label.set_size(result.m_NumberOfLabels);
for(unsigned int i = 0; i < rois.size(); ++i)
{
result.m_MaxIndexX[i] = GetIntegerAttribute(rois[i], "maximumIndexX");
result.m_MinIndexX[i] = GetIntegerAttribute(rois[i], "minimumIndexX");
result.m_MaxIndexY[i] = GetIntegerAttribute(rois[i], "maximumIndexY");
result.m_MinIndexY[i] = GetIntegerAttribute(rois[i], "minimumIndexY");
result.m_MaxIndexZ[i] = GetIntegerAttribute(rois[i], "maximumIndexZ");
result.m_MinIndexZ[i] = GetIntegerAttribute(rois[i], "minimumIndexZ");
result.m_Label[i] = GetIntegerAttribute(rois[i], "label");
}
// read statistic parameters, fill result structure
NodeList statistics;
domRoot->GetChildren("statistic", statistics);
MITK_TEST_CONDITION_REQUIRED( statistics.size() >= 1 , "At least one statistic defined" )
MITK_TEST_CONDITION_REQUIRED( statistics.size() == rois.size(), "Same number of rois and corresponding statistics defined");
result.m_HotspotMin.set_size(statistics.size());
result.m_HotspotMax.set_size(statistics.size());
result.m_HotspotMean.set_size(statistics.size());
result.m_HotspotMinIndexX.set_size(statistics.size());
result.m_HotspotMinIndexY.set_size(statistics.size());
result.m_HotspotMinIndexZ.set_size(statistics.size());
result.m_HotspotMaxIndexX.set_size(statistics.size());
result.m_HotspotMaxIndexY.set_size(statistics.size());
result.m_HotspotMaxIndexZ.set_size(statistics.size());
result.m_HotspotIndexX.set_size(statistics.size());
result.m_HotspotIndexY.set_size(statistics.size());
result.m_HotspotIndexZ.set_size(statistics.size());
for(unsigned int i = 0; i < statistics.size(); ++i)
{
result.m_HotspotMin[i] = GetDoubleAttribute(statistics[i], "minimum");
result.m_HotspotMax[i] = GetDoubleAttribute(statistics[i], "maximum");
result.m_HotspotMean[i] = GetDoubleAttribute(statistics[i], "mean");
result.m_HotspotMinIndexX[i] = GetIntegerAttribute(statistics[i], "minimumIndexX");
result.m_HotspotMinIndexY[i] = GetIntegerAttribute(statistics[i], "minimumIndexY");
result.m_HotspotMinIndexZ[i] = GetIntegerAttribute(statistics[i], "minimumIndexZ");
result.m_HotspotMaxIndexX[i] = GetIntegerAttribute(statistics[i], "maximumIndexX");
result.m_HotspotMaxIndexY[i] = GetIntegerAttribute(statistics[i], "maximumIndexY");
result.m_HotspotMaxIndexZ[i] = GetIntegerAttribute(statistics[i], "maximumIndexZ");
result.m_HotspotIndexX[i] = GetIntegerAttribute(statistics[i], "hotspotIndexX");
result.m_HotspotIndexY[i] = GetIntegerAttribute(statistics[i], "hotspotIndexY");
result.m_HotspotIndexZ[i] = GetIntegerAttribute(statistics[i], "hotspotIndexZ");
}
}
catch (std::exception& e)
{
MITK_TEST_CONDITION_REQUIRED(false, "Reading test parameters from XML file. Error message: " << e.what());
}
return result;
}
/**
\brief Generate an image that contains a couple of 3D gaussian distributions.
Uses the given parameters to produce a test image using class MultiGaussianImageSource.
*/
static mitk::Image::Pointer BuildTestImage(const Parameters& testParameters)
{
typedef double PixelType;
const int Dimension = 3;
typedef itk::Image<PixelType, Dimension> ImageType;
typedef itk::MultiGaussianImageSource< ImageType > MultiGaussianImageSource;
MultiGaussianImageSource::Pointer gaussianGenerator = MultiGaussianImageSource::New();
ImageType::SizeValueType size[3];
size[0] = testParameters.m_ImageColumns;
size[1] = testParameters.m_ImageRows;
size[2] = testParameters.m_ImageSlices;
itk::MultiGaussianImageSource<ImageType>::VectorType centerXVec, centerYVec, centerZVec, sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec;
for(int i = 0; i < testParameters.m_NumberOfGaussian; ++i)
{
centerXVec.push_back(testParameters.m_CenterX[i]);
centerYVec.push_back(testParameters.m_CenterY[i]);
centerZVec.push_back(testParameters.m_CenterZ[i]);
sigmaXVec.push_back(testParameters.m_SigmaX[i]);
sigmaYVec.push_back(testParameters.m_SigmaY[i]);
sigmaZVec.push_back(testParameters.m_SigmaZ[i]);
altitudeVec.push_back(testParameters.m_Altitude[i]);
}
ImageType::SpacingType spacing;
for( int i = 0; i < Dimension; ++i )
spacing[i] = testParameters.m_Spacing[i];
gaussianGenerator->SetSize( size );
gaussianGenerator->SetSpacing( spacing );
gaussianGenerator->SetRadius(testParameters.m_HotspotRadiusInMM);
gaussianGenerator->SetNumberOfGausssians(testParameters.m_NumberOfGaussian);
gaussianGenerator->AddGaussian(centerXVec, centerYVec, centerZVec,
sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec);
gaussianGenerator->Update();
return mitk::GrabItkImageMemory(gaussianGenerator->GetOutput(), nullptr, nullptr, false);
}
/**
\brief Calculates hotspot statistics for given test image and ROI parameters.
Uses ImageStatisticsCalculator to find a hotspot in a defined ROI within the given image.
*/
static mitk::ImageStatisticsContainer::ImageStatisticsObject CalculateStatistics(mitk::Image* image, const Parameters& testParameters, unsigned int label)
{
const unsigned int Dimension = 3;
typedef itk::Image<unsigned short, Dimension> MaskImageType;
MaskImageType::Pointer mask = MaskImageType::New();
MaskImageType::SizeType size;
MaskImageType::SpacingType spacing;
MaskImageType::IndexType start;
mitk::ImageStatisticsCalculator::Pointer statisticsCalculator = mitk::ImageStatisticsCalculator::New();
statisticsCalculator->SetInputImage(image);
if((testParameters.m_MaxIndexX[label] > testParameters.m_MinIndexX[label] && testParameters.m_MinIndexX[label] >= 0) &&
(testParameters.m_MaxIndexY[label] > testParameters.m_MinIndexY[label] && testParameters.m_MinIndexY[label] >= 0) &&
(testParameters.m_MaxIndexZ[label] > testParameters.m_MinIndexZ[label] && testParameters.m_MinIndexZ[label] >= 0))
{
for(unsigned int i = 0; i < Dimension; ++i)
{
start[i] = 0;
spacing[i] = testParameters.m_Spacing[i];
}
size[0] = testParameters.m_ImageColumns;
size[1] = testParameters.m_ImageRows;
size[2] = testParameters.m_ImageSlices;
MaskImageType::RegionType region;
region.SetIndex(start);
region.SetSize(size);
mask->SetSpacing(spacing);
mask->SetRegions(region);
mask->Allocate();
typedef itk::ImageRegionIteratorWithIndex<MaskImageType> MaskImageIteratorType;
MaskImageIteratorType maskIt(mask, region);
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
maskIt.Set(0);
}
for(unsigned int i = 0; i < testParameters.m_NumberOfLabels; ++i)
{
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
MaskImageType::IndexType index = maskIt.GetIndex();
if((index[0] >= testParameters.m_MinIndexX[i] && index[0] <= testParameters.m_MaxIndexX[i] ) &&
(index[1] >= testParameters.m_MinIndexY[i] && index[1] <= testParameters.m_MaxIndexY[i] ) &&
(index[2] >= testParameters.m_MinIndexZ[i] && index[2] <= testParameters.m_MaxIndexZ[i] ))
{
maskIt.Set(testParameters.m_Label[i]);
}
}
}
auto mitkMaskImage = mitk::GrabItkImageMemory(mask, nullptr, nullptr, false);
mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
imgMaskGen->SetInputImage(image);
imgMaskGen->SetImageMask(mitkMaskImage);
mitk::HotspotMaskGenerator::Pointer hotspotMaskGen = mitk::HotspotMaskGenerator::New();
hotspotMaskGen->SetInputImage(image);
hotspotMaskGen->SetLabel(testParameters.m_Label[label]);
hotspotMaskGen->SetMask(imgMaskGen.GetPointer());
hotspotMaskGen->SetHotspotRadiusInMM(testParameters.m_HotspotRadiusInMM);
if(testParameters.m_EntireHotspotInImage == 1)
{
- MITK_INFO << "Hotspot must be completly inside image";
+ MITK_INFO << "Hotspot must be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(true);
}
else
{
- MITK_INFO << "Hotspot must not be completly inside image";
+ MITK_INFO << "Hotspot must not be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(false);
}
statisticsCalculator->SetMask(hotspotMaskGen.GetPointer());
MITK_DEBUG << "Masking is set to hotspot+image mask";
}
else
{
mitk::HotspotMaskGenerator::Pointer hotspotMaskGen = mitk::HotspotMaskGenerator::New();
hotspotMaskGen->SetInputImage(image);
hotspotMaskGen->SetHotspotRadiusInMM(testParameters.m_HotspotRadiusInMM);
if(testParameters.m_EntireHotspotInImage == 1)
{
- MITK_INFO << "Hotspot must be completly inside image";
+ MITK_INFO << "Hotspot must be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(true);
}
else
{
- MITK_INFO << "Hotspot must not be completly inside image";
+ MITK_INFO << "Hotspot must not be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(false);
}
MITK_DEBUG << "Masking is set to hotspot only";
}
return statisticsCalculator->GetStatistics()->GetStatistics(label,0);
}
static void ValidateStatisticsItem(const std::string& label, double testvalue, double reference, double tolerance)
{
double diff = ::fabs(reference - testvalue);
MITK_TEST_CONDITION( diff < tolerance, "'" << label << "' value close enough to reference value "
"(value=" << testvalue <<
", reference=" << reference <<
", diff=" << diff << ")" );
}
static void ValidateStatisticsItem(const std::string& label, const vnl_vector<int>& testvalue, const vnl_vector<int>& reference)
{
double diffX = ::fabs(double(testvalue[0] - reference[0]));
double diffY = ::fabs(double(testvalue[1] - reference[1]));
double diffZ = ::fabs(double(testvalue[2] - reference[2]));
std::stringstream testPosition;
testPosition << testvalue[0] << "," << testvalue[1] << "," << testvalue[2];
std::stringstream referencePosition;
referencePosition << reference[0] << "," << reference[1] << "," << reference[2];
MITK_TEST_CONDITION( diffX < mitk::eps && diffY < mitk::eps && diffZ < mitk::eps,
"'" << label << "' close enough to reference value " <<
"(value=[" << testPosition.str() << "]," <<
" reference=[" << referencePosition.str() << "]");
}
/**
\brief Compares calculated against actual statistics values.
Checks validness of all statistics aspects. Lets test fail if any aspect is not sufficiently equal.
*/
static void ValidateStatistics(const mitk::ImageStatisticsContainer::ImageStatisticsObject hotspotStatistics, const Parameters& testParameters, unsigned int label)
{
// check all expected test result against actual results
double eps = 0.25; // value above the largest tested difference
auto mean = hotspotStatistics.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN());
auto max = hotspotStatistics.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MAXIMUM());
auto min = hotspotStatistics.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MINIMUM());
ValidateStatisticsItem("Hotspot mean", mean, testParameters.m_HotspotMean[label], eps);
ValidateStatisticsItem("Hotspot maximum", max, testParameters.m_HotspotMax[label], eps);
ValidateStatisticsItem("Hotspot minimum", min, testParameters.m_HotspotMin[label], eps);
vnl_vector<int> referenceHotspotCenterIndex; referenceHotspotCenterIndex.set_size(3);
referenceHotspotCenterIndex[0] = testParameters.m_HotspotIndexX[label];
referenceHotspotCenterIndex[1] = testParameters.m_HotspotIndexY[label];
referenceHotspotCenterIndex[2] = testParameters.m_HotspotIndexZ[label];
// ValidateStatisticsItem("Hotspot center position", statistics.GetHotspotStatistics().GetHotspotIndex(), referenceHotspotCenterIndex); TODO: new image statistics calculator does not give hotspot position
// TODO we do not test minimum/maximum positions within the peak/hotspot region, because
// these positions are not unique, i.e. there are multiple valid minima/maxima positions.
- // One solution would be to modify the test cases in order to achive clear positions.
+ // One solution would be to modify the test cases in order to achieve clear positions.
// The BETTER/CORRECT solution would be to change the singular position into a set of positions / a region
}
};
/**
\brief Verifies that hotspot statistics part of ImageStatisticsCalculator.
The test reads parameters from an XML-file to generate a test-image, calculates the hotspot statistics of the image
and checks if the calculated statistics are the same as the specified values of the XML-file.
*/
int mitkImageStatisticsHotspotTest(int argc, char* argv[])
{
MITK_TEST_BEGIN("mitkImageStatisticsHotspotTest")
try {
mitkImageStatisticsHotspotTestClass::Parameters parameters = mitkImageStatisticsHotspotTestClass::ParseParameters(argc,argv);
mitk::Image::Pointer image = mitkImageStatisticsHotspotTestClass::BuildTestImage(parameters);
MITK_TEST_CONDITION_REQUIRED( image.IsNotNull(), "Generate test image" );
for(unsigned int label = 0; label < parameters.m_NumberOfLabels; ++label)
{
mitk::ImageStatisticsContainer::ImageStatisticsObject statistics = mitkImageStatisticsHotspotTestClass::CalculateStatistics(image, parameters, label);
mitkImageStatisticsHotspotTestClass::ValidateStatistics(statistics, parameters, label);
std::cout << std::endl;
}
}
catch (std::exception& e)
{
std::cout << "Error: " << e.what() << std::endl;
MITK_TEST_CONDITION_REQUIRED( false, "Exception occurred during test execution: " << e.what() );
}
catch(...)
{
MITK_TEST_CONDITION_REQUIRED( false, "Exception occurred during test execution." );
}
MITK_TEST_END()
}
diff --git a/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp b/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp
index 26e0c1e563..9d3df1f259 100644
--- a/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp
@@ -1,542 +1,542 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include "itkMultiGaussianImageSource.h"
#include <string>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <itkDOMNode.h>
#include <itkDOMNodeXMLWriter.h>
#include <itkDOMNodeXMLReader.h>
#include <itkImage.h>
-// Commandline:(for exmaple) mitkMultiGaussianTest C:/temp/output C:/temp/inputFile.xml
+// Commandline:(for example) mitkMultiGaussianTest C:/temp/output C:/temp/inputFile.xml
//
//For Example: inputFile.xml
//<testcase>
// <testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="1" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
// <gaussian centerIndexX="10" centerIndexY="10" centerIndexZ="10" deviationX="6" deviationY="6" deviationZ="6" altitude="200"/>
// </testimage>
//<segmentation numberOfLabels="2" hotspotRadiusInMM="6.2035">
// <roi label="1" maximumIndexX="20" minimumIndexX="12" maximumIndexY="20" minimumIndexY="12" maximumIndexZ="20" minimumIndexZ="12"/>
// <roi label="2" maximumIndexX="10" minimumIndexX="0" maximumIndexY="10" minimumIndexY="0" maximumIndexZ="10" minimumIndexZ="0"/>
// </segmentation>
//</testcase>
//
//For Example: output.xml
// <testcase>
// <testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="1" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
// <gaussian centerIndexX="50" centerIndexY="50" centerIndexZ="50" deviationX="5" deviationY="5" deviationZ="5" altitude="200"/>
// <gaussian centerIndexX="46" centerIndexY="46" centerIndexZ="46" deviationX="40" deviationY="40" deviationZ="40" altitude="170"/>
// </testimage>
// <segmentation numberOfLabels="1" hotspotRadiusInMM="8">
// <roi hotspotRadiusInMM="6.2035" maximumIndexX="122" minimumIndexX="7" maximumIndexY="122" minimumIndexY="7" maximumIndexZ="60" minimumIndexZ="4"/>
// </segmentation>
// <statistic hotspotIndexX="50" hotspotIndexY="50" hotspotIndexZ="25" peak="291.067" mean="291.067" maximumIndexX="50" maximumIndexY="50" maximumIndexZ="25" maximum="367.469" minimumIndexX="55" minimumIndexY="53" minimumIndexZ="26" minimum="254.939"/>
//</testcase>
bool IsInOtherROI(int,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType);
int mitkMultiGaussianTest(int argc, char* argv[])
{
- // Read the parmaeter from a .xml File.
+ // Read the parameter from an .xml File.
// In the inputFile.xml we find the characteristics of the Gaussian and the ROI's. Hier we can have more then one ROI -> we find the hot spot for each of the ROI's; we can set the entire HotSpot to be in the image or just its midpoint, but not necessary the whole HotSpot.
const unsigned int Dimension = 3;
typedef double PixelType;
typedef itk::DOMNode::Pointer DOMNodeType;
typedef itk::Image<PixelType, Dimension> ImageType;
typedef itk::MultiGaussianImageSource< ImageType > MultiGaussianImageSource;
std::string outputFilename = argv[1], name;
int numberOfImages;
double centerX, centerY, centerZ, sigmaX, sigmaY, sigmaZ, altitude, hotSpotRadiusInMM;
unsigned int numberOfGaussians, minWidthOfGaussian, maxWidthOfGaussian, minAltitudeOfGaussian, maxAltitudeOfGaussian, numberOfLabels;
itk::MultiGaussianImageSource< ImageType >::VectorType centerXVec, centerYVec, centerZVec, sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec, ROImaxIndexX, ROIminIndexX, ROImaxIndexY, ROIminIndexY, ROImaxIndexZ, ROIminIndexZ, label;
itk::MultiGaussianImageSource< ImageType >::ItkVectorType regionOfInterestMax, regionOfInterestMin;
itk::MultiGaussianImageSource< ImageType >::IndexType sphereMidpt, maxValueIndexInSphere, minValueIndexInSphere;
MultiGaussianImageSource::Pointer gaussianGenerator;
itk::DOMNodeXMLWriter::Pointer xmlWriter;
itk::MultiGaussianImageSource< ImageType >::SpacingValueArrayType spacing;
DOMNodeType domTestCase, domTestImage, domGaussian, domSegmentation, domStatistics, domROI;
ImageType::SizeValueType size[3];
std::stringstream ss;
double radius = pow(itk::Math::one_over_pi * 0.75 , 1.0 / 3.0) * 10;
char * fileNamePointer;
std::string attributeValue;
double value;
bool entireHotSpotInImage;
int maxIndex, minIndex;
std::string filename = argv[2];
itk::DOMNodeXMLReader::Pointer xmlReader = itk::DOMNodeXMLReader::New();
xmlReader->SetFileName( filename );
xmlReader->Update();
itk::DOMNode::Pointer domRoot = xmlReader->GetOutput();
typedef std::vector<itk::DOMNode*> NodeList;
// read test image parameters, fill result structure
NodeList testimages;
domRoot->GetChildren("testimage", testimages);
MITK_TEST_CONDITION_REQUIRED( testimages.size() == 1, "One test image defined" )
itk::DOMNode* testimage = testimages[0];
attributeValue = testimage->GetAttribute("image-rows");
std::stringstream(attributeValue) >> size[0];
attributeValue = testimage->GetAttribute("image-columns");
std::stringstream(attributeValue) >> size[1];
attributeValue = testimage->GetAttribute("image-slices");
std::stringstream(attributeValue) >> size[2];
attributeValue = testimage->GetAttribute( "numberOfGaussians" );
std::stringstream(attributeValue) >> numberOfGaussians;
attributeValue = testimage->GetAttribute( "spacingX" );
std::stringstream(attributeValue) >> spacing[0];
attributeValue = testimage->GetAttribute( "spacingY" );
std::stringstream(attributeValue) >> spacing[1];
attributeValue = testimage->GetAttribute( "spacingZ" );
std::stringstream(attributeValue) >> spacing[2];
attributeValue = testimage->GetAttribute( "entireHotSpotInImage" );
std::stringstream(attributeValue) >> entireHotSpotInImage;
std::cout << "Read size parameters (x,y,z): " << size[0] << ", " << size[1] << ", " << size[2] << "\n" << std::endl;
std::cout << "Read spacing parameters (x,y,z): " << spacing[0] << ", " << spacing[1] << ", " << spacing[2]<< "\n" << std::endl;
NodeList gaussians;
testimage->GetChildren("gaussian", gaussians);
itk::DOMNode* gaussian;
for(int i = 0; i < numberOfGaussians ; ++i)
{
gaussian = gaussians[i];
//TODO
attributeValue = gaussian->GetAttribute( "centerIndexX" );
std::stringstream(attributeValue) >> value;
centerXVec.push_back(value * spacing[0]);
attributeValue = gaussian->GetAttribute( "centerIndexY" );
std::stringstream(attributeValue) >> value;
centerYVec.push_back(value * spacing[1]);
attributeValue = gaussian->GetAttribute( "centerIndexZ" );
std::stringstream(attributeValue) >> value;
centerZVec.push_back(value * spacing[2]);
std::cout << "Read center of Gaussian (x,y,z) in mm: " << centerXVec[i] << ", " << centerYVec[i] << ", " << centerZVec[i] << "\n" << std::endl;
attributeValue = gaussian->GetAttribute( "deviationX" );
std::stringstream(attributeValue) >> value;
sigmaXVec.push_back(value * spacing[0]);
attributeValue = gaussian->GetAttribute( "deviationY" );
std::stringstream(attributeValue) >> value;
sigmaYVec.push_back(value * spacing[1]);
attributeValue = gaussian->GetAttribute( "deviationZ" );
std::stringstream(attributeValue) >> value;
sigmaZVec.push_back(value * spacing[2]);
std::cout << "Read deviation of Gaussian (x,y,z) in mm: " << sigmaXVec[i] << ", " << sigmaYVec[i] << ", " << sigmaZVec[i] << "\n" << std::endl;
attributeValue = gaussian->GetAttribute( "altitude" );
std::stringstream(attributeValue) >> value;
altitudeVec.push_back(value);
std::cout << "Read altitude: " << altitudeVec[i] << "\n" << std::endl;
}
// read ROI's parameter
NodeList segmentations;
domRoot->GetChildren("segmentation", segmentations);
MITK_TEST_CONDITION_REQUIRED( segmentations.size() == 1, "One ROI image defined" )
itk::DOMNode* segmentation = segmentations[0];
attributeValue = segmentation->GetAttribute("numberOfLabels");
std::stringstream(attributeValue) >> numberOfLabels;
attributeValue = segmentation->GetAttribute("hotspotRadiusInMM");
std::stringstream(attributeValue) >> hotSpotRadiusInMM;
std::cout << "Read number of labels: " << numberOfLabels << std::endl;
std::cout << "Read radius in mm : " << hotSpotRadiusInMM << std::endl;
NodeList rois;
segmentation->GetChildren("roi", rois);
itk::DOMNode* roi;
// for each label i take the ROI and set it to be the i'th element of ROImaxIndex* and ROIminIndex* ( * = X, Y, Z)
for(int i = 0; i < numberOfLabels ; ++i)
{
roi = rois[i];
attributeValue = roi->GetAttribute( "label" );
std::stringstream(attributeValue) >> value;
label.push_back(value);
attributeValue = roi->GetAttribute( "maximumIndexX" );
std::stringstream(attributeValue) >> value;
ROImaxIndexX.push_back(value);
attributeValue = roi->GetAttribute( "minimumIndexX" );
std::stringstream(attributeValue) >> value;
ROIminIndexX.push_back(value);
attributeValue = roi->GetAttribute( "maximumIndexY" );
std::stringstream(attributeValue) >> value;
ROImaxIndexY.push_back(value);
attributeValue = roi->GetAttribute( "minimumIndexY" );
std::stringstream(attributeValue) >> value;
ROIminIndexY.push_back(value);
attributeValue = roi->GetAttribute( "maximumIndexZ" );
std::stringstream(attributeValue) >> value;
ROImaxIndexZ.push_back(value);
attributeValue = roi->GetAttribute( "minimumIndexZ" );
std::stringstream(attributeValue) >> value;
ROIminIndexZ.push_back(value);
std::cout << "Read ROI with label number: " << label[i] << " with min and max values in the x-, y-, z-Achse: [" << ROIminIndexX[i] << " " << ROImaxIndexX[i] <<"], [" << ROIminIndexY[i] << " " << ROImaxIndexY[i] <<"], [" << ROIminIndexZ[i] << " " << ROImaxIndexZ[i] <<"]\n" << std::endl;
}
// Check whether the ROI's are correct defined, i.e. whether the ROI's are disjoint
for(int i = 1; i < numberOfLabels ; ++i)
{
// check whether the edges of the i'th ROI is in another ROI included (when yes -> ERROR)
bool isInOtherROI = IsInOtherROI( i, ROIminIndexX, ROImaxIndexX, ROIminIndexY, ROImaxIndexY, ROIminIndexZ, ROImaxIndexZ );
if( isInOtherROI)
{
std::cout << "The ROI's in the different labels should be disjoint! Please define it correct. " << std::endl;
return 0;
}
}
//write test image parameter
xmlWriter = itk::DOMNodeXMLWriter::New();
domTestCase = itk::DOMNode::New();
domTestCase->SetName("testcase");
domTestImage = itk::DOMNode::New();
domTestImage->SetName("testimage");
ss.str("");
ss << size[0];
domTestImage->SetAttribute("image-rows", ss.str());
ss.str("");
ss << size[1];
domTestImage->SetAttribute("image-columns", ss.str());
ss.str("");
ss << size[2];
domTestImage->SetAttribute("image-slices", ss.str());
ss.str("");
ss << numberOfGaussians;
domTestImage->SetAttribute("numberOfGaussians", ss.str());
ss.str("");
ss << spacing[0];
domTestImage->SetAttribute("spacingX", ss.str());
ss.str("");
ss << spacing[1];
domTestImage->SetAttribute("spacingY", ss.str());
ss.str("");
ss << spacing[2];
domTestImage->SetAttribute("spacingZ", ss.str());
ss.str("");
ss << entireHotSpotInImage;
domTestImage->SetAttribute("entireHotSpotInImage", ss.str());
domTestCase->AddChildAtBegin(domTestImage);
for( unsigned int i = 0; i < numberOfGaussians; ++i)
{
domGaussian = itk::DOMNode::New() ;
domGaussian->SetName("gaussian");
domTestImage->AddChildAtEnd(domGaussian);
// write the midpoint and the daviation in pixel units
centerX = centerXVec[i] / spacing[0];
ss.str("");
ss << centerX; // * spacing[0]; //static_cast<double>( static_cast<int>( centerX / spacing[0] + 0.9999 ) );
domGaussian->SetAttribute("centerIndexX", ss.str());
centerY = centerYVec[i] / spacing[1];
ss.str("");
ss << centerY; // * spacing[1]; //static_cast<double>( static_cast<int>( centerY / spacing[1] + 0.9999 ) );
domGaussian->SetAttribute("centerIndexY", ss.str());
centerZ = centerZVec[i] / spacing[2];
ss.str("");
ss << centerZ; // * spacing[2]; //static_cast<double>( static_cast<int>( centerZ / spacing[2] + 0.9999 ) );
domGaussian->SetAttribute("centerIndexZ", ss.str());
sigmaX = sigmaXVec[i] / spacing[0];
ss.str("");
ss << sigmaX; // * spacing[0]; // static_cast<double>( static_cast<int>( sigmaX / spacing[0] + 0.9999 ) );
domGaussian->SetAttribute("deviationX", ss.str());
sigmaY = sigmaYVec[i] / spacing[1];
ss.str("");
ss << sigmaY; // * spacing[1]; //static_cast<double>( static_cast<int>( sigmaY / spacing[1] + 0.9999 ) );
domGaussian->SetAttribute("deviationY", ss.str());
sigmaZ = sigmaZVec[i] / spacing[2];
ss.str("");
ss << sigmaZ; // * spacing[2]; //static_cast<double>( static_cast<int>( sigmaZ / spacing[2] + 0.9999 ) );
domGaussian->SetAttribute("deviationZ", ss.str());
altitude = altitudeVec[i];
ss.str("");
ss << altitude;
domGaussian->SetAttribute("altitude", ss.str());
}
radius = hotSpotRadiusInMM;
// set the parameter for the gaussianGenerator
gaussianGenerator = MultiGaussianImageSource::New();
gaussianGenerator->SetSize( size );
gaussianGenerator->SetSpacing( spacing );
gaussianGenerator->SetRadius(radius);
gaussianGenerator->SetNumberOfGausssians(numberOfGaussians);
gaussianGenerator->AddGaussian(centerXVec, centerYVec, centerZVec, sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec);
domSegmentation = itk::DOMNode::New();
domSegmentation->SetName("segmentation");
domTestCase->AddChildAtEnd(domSegmentation);
ss.str("");
ss << numberOfLabels;
domSegmentation->SetAttribute("numberOfLabels", ss.str());
ss.str("");
ss << hotSpotRadiusInMM;
domSegmentation->SetAttribute("hotspotRadiusInMM", ss.str());
// set the region of interest for each label i
for (unsigned int i = 0; i < numberOfLabels; ++i)
{
// Set region of interest in index values. The entire HotSpot is in the image.
if(entireHotSpotInImage)
{
// x axis region of interest------------------------------------------------------
minIndex = 0.0 + static_cast<int>((radius)/spacing[0]+ 0.5);
maxIndex = size[0]-1-minIndex;
if( minIndex >= maxIndex )
{
std::cout << "The sphere is larger then the image in the x axis!" << std::endl;
}
// the maximum in the x-Axis
regionOfInterestMax.SetElement( 0, ( ROImaxIndexX[i] < maxIndex ) ? ROImaxIndexX[i] : maxIndex );
// the minimum in the x-Axis
regionOfInterestMin.SetElement( 0, ( ROIminIndexX[i] > minIndex ) ? ROIminIndexX[i] : minIndex );
// y axis region of interest------------------------------------------------------
minIndex = 0.0 + static_cast<int>((radius)/spacing[1]+ 0.5);
maxIndex = size[1]-1-minIndex;
if( minIndex >= maxIndex )
{
std::cout << "The sphere is larger then the image in the y axis!" << std::endl;
}
// the maximum in the y-Axis
regionOfInterestMax.SetElement( 1, ( ROImaxIndexY[i] < maxIndex ) ? ROImaxIndexY[i] : maxIndex );
// the minimum in the y-Axis
regionOfInterestMin.SetElement( 1, ( ROIminIndexY[i] > minIndex ) ? ROIminIndexY[i] : minIndex );
// z axis region of interest------------------------------------------------------
minIndex = 0.0 + static_cast<int>((radius)/spacing[2]+ 0.5); // int(6.2/3.0 + 0.5) = 2
maxIndex = size[2]-1-minIndex;
if( minIndex >= maxIndex )
{
std::cout << "The sphere is larger then the image in the z axis!" << std::endl;
}
// the maximum in the z-Axis
regionOfInterestMax.SetElement( 2, ( ROImaxIndexZ[i] < maxIndex ) ? ROImaxIndexZ[i] : maxIndex );
// the minimum in the z-Axis
regionOfInterestMin.SetElement( 2, ( ROIminIndexZ[i] > minIndex ) ? ROIminIndexZ[i] : minIndex );
}
// Set region of interest in index values. The midpoint of the HotSpot is in the image, but not necessary the whole HotSpot
else
{
// x axis region of interest------------------------------------------------------
regionOfInterestMax.SetElement( 0, ROImaxIndexX[i] );
regionOfInterestMin.SetElement( 0, ROIminIndexX[i] );
// y axis region of interest------------------------------------------------------
regionOfInterestMax.SetElement( 1, ROImaxIndexY[i] );
regionOfInterestMin.SetElement( 1, ROIminIndexY[i] );
// z axis region of interest------------------------------------------------------
regionOfInterestMax.SetElement( 2, ROImaxIndexZ[i] );
regionOfInterestMin.SetElement( 2, ROIminIndexZ[i] );
}
gaussianGenerator->SetRegionOfInterest(regionOfInterestMin, regionOfInterestMax);
gaussianGenerator->Update();
//write region of interest for the .xml file
domROI = itk::DOMNode::New();
domROI->SetName("roi");
domSegmentation->AddChildAtEnd(domROI);
ss.str("");
ss << label[i];
domROI->SetAttribute("label", ss.str());
ss.str("");
ss << ROImaxIndexX[i];
domROI->SetAttribute("maximumIndexX", ss.str());
ss.str("");
ss << ROIminIndexX[i];
domROI->SetAttribute("minimumIndexX", ss.str());
ss.str("");
ss << ROImaxIndexY[i];
domROI->SetAttribute("maximumIndexY", ss.str());
ss.str("");
ss << ROIminIndexY[i];
domROI->SetAttribute("minimumIndexY", ss.str());
ss.str("");
ss << ROImaxIndexZ[i];
domROI->SetAttribute("maximumIndexZ", ss.str());
ss.str("");
ss << ROIminIndexZ[i];
domROI->SetAttribute("minimumIndexZ", ss.str());
// Calculate the mean value and the midpoint of the wanted sphere.
gaussianGenerator->CalculateTheMidpointAndTheMeanValueWithOctree();
//peak and peak coordinate
domStatistics = itk::DOMNode::New();
domStatistics->SetName("statistic");
domTestCase->AddChildAtEnd(domStatistics);
sphereMidpt = gaussianGenerator->GetSphereMidpoint();
ss.str("");
ss << sphereMidpt[0];
domStatistics->SetAttribute("hotspotIndexX", ss.str());
ss.str("");
ss << sphereMidpt[1];
domStatistics->SetAttribute("hotspotIndexY", ss.str());
ss.str("");
ss << sphereMidpt[2];
domStatistics->SetAttribute("hotspotIndexZ", ss.str());
ss.str("");
ss << gaussianGenerator->GetMaxMeanValue();
domStatistics->SetAttribute("mean", ss.str());
//maximum and maximum coordinate
gaussianGenerator->CalculateMaxAndMinInSphere();
maxValueIndexInSphere = gaussianGenerator->GetMaxValueIndexInSphere();
ss.str("");
ss << maxValueIndexInSphere[0];
domStatistics->SetAttribute("maximumIndexX", ss.str());
ss.str("");
ss << maxValueIndexInSphere[1];
domStatistics->SetAttribute("maximumIndexY", ss.str());
ss.str("");
ss << maxValueIndexInSphere[2];
domStatistics->SetAttribute("maximumIndexZ", ss.str());
ss.str("");
ss << gaussianGenerator->GetMaxValueInSphere();
domStatistics->SetAttribute("maximum", ss.str());
//minimum and minimum coordinate
minValueIndexInSphere = gaussianGenerator->GetMinValueIndexInSphere();
ss.str("");
ss << minValueIndexInSphere[0];
domStatistics->SetAttribute("minimumIndexX", ss.str());
ss.str("");
ss << minValueIndexInSphere[1];
domStatistics->SetAttribute("minimumIndexY", ss.str());
ss.str("");
ss << minValueIndexInSphere[2];
domStatistics->SetAttribute("minimumIndexZ", ss.str());
ss.str("");
ss << gaussianGenerator->GetMinValueInSphere();
domStatistics->SetAttribute("minimum", ss.str());
}
// .xml (Data)
ss.str("");
ss << outputFilename << ".xml";
name = ss.str();
fileNamePointer = (char*) name.c_str();
xmlWriter->SetFileName( fileNamePointer);
xmlWriter->SetInput( domTestCase );
xmlWriter->Update();
ImageType::Pointer gaussianImage = gaussianGenerator->GetOutput();
//.nrrd (Image)
typedef itk::ImageFileWriter< ImageType > WriterType;
WriterType::Pointer writer = WriterType::New();
ss.str("");
ss << outputFilename << ".nrrd";
name = ss.str();
fileNamePointer = (char*) name.c_str();
writer->SetFileName( fileNamePointer);
writer->SetInput( gaussianImage );
writer->Update();
}
// check whether the edges of the i'th ROI is in another ROI included
bool IsInOtherROI(int i,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROIminIndexX,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROImaxIndexX,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROIminIndexY,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROImaxIndexY,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROIminIndexZ,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROImaxIndexZ )
{
bool error = 0;
std::vector<double> xBound, yBound, zBound;
xBound.push_back( ROIminIndexX[i] );
xBound.push_back( ROImaxIndexX[i] );
yBound.push_back( ROIminIndexY[i] );
yBound.push_back( ROImaxIndexY[i] );
zBound.push_back( ROIminIndexZ[i] );
zBound.push_back( ROImaxIndexZ[i] );
//for each ROI
for( unsigned int j = 0; j < i; ++j )
{
for( unsigned int x = 0; x < 2; ++x)
{
for( unsigned int y = 0; y < 2; ++y)
{
for( unsigned int z = 0; z < 2; ++z)
{
double edgeXCoord = xBound[x];
double edgeYCoord = yBound[y];
double edgeZCoord = zBound[z];
// check if the edge with coordinate [edgeXCoord; edgeYCoord; edgeZCoord] is inside the j'th ROI
if ( ROIminIndexX[j] < edgeXCoord && edgeXCoord < ROImaxIndexX[j] &&
ROIminIndexY[j] < edgeYCoord && edgeYCoord < ROImaxIndexY[j] &&
ROIminIndexZ[j] < edgeZCoord && edgeZCoord < ROImaxIndexZ[j])
{
error = 1;
return error;
}
}
}
}
}
return error;
}
diff --git a/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.cpp
index c1ad7cc852..ed33ae0676 100644
--- a/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.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 "mitkTestingMacros.h"
#include "mitkPointSetDifferenceStatisticsCalculator.h"
/**
* \brief Test class for mitkPointSetDifferenceStatisticsCalculator
*/
//Members used for testing purposes
mitk::PointSetDifferenceStatisticsCalculator::Pointer m_myPointSetDifferenceStatisticsCalculator;
mitk::PointSet::Pointer m_myTestPointSet1;
mitk::PointSet::Pointer m_myTestPointSet2;
-//This method should be called before every new sub-test call in order to freshly intialize all relevant classes
+//This method should be called before every new sub-test call in order to freshly initialize all relevant classes
void Setup()
{
// let's create an object of our class
m_myPointSetDifferenceStatisticsCalculator = mitk::PointSetDifferenceStatisticsCalculator::New();
//and some empty test data
m_myTestPointSet1 = mitk::PointSet::New();
m_myTestPointSet2 = mitk::PointSet::New();
}
void PointSetDifferenceStatisticsCalculator_DefaultConstructor_ResultIsNotNull()
{
Setup();
// let's create an object of our class
MITK_TEST_CONDITION_REQUIRED(m_myPointSetDifferenceStatisticsCalculator.IsNotNull(),"Testing instantiation with default constructor.");
}
void PointSetDifferenceStatisticsCalculator_NonDefaultConstructor_ResultIsNotNull()
{
Setup();
m_myPointSetDifferenceStatisticsCalculator = mitk::PointSetDifferenceStatisticsCalculator::New(m_myTestPointSet1,m_myTestPointSet2);
MITK_TEST_CONDITION_REQUIRED(m_myPointSetDifferenceStatisticsCalculator.IsNotNull(),"Testing instantiation with non default constructor.");
}
void PointSetDifferenceStatisticsCalculator_TwoSimplePointSetsOfSizeTwo_ResultsInGroundTruthValues()
{
MITK_TEST_OUTPUT(<< "Starting simple test case...");
mitk::Point3D tmpPoint;
//fill the point sets with simple test data
mitk::FillVector3D(tmpPoint,0,0,0);
m_myTestPointSet1->InsertPoint(0,tmpPoint);
mitk::FillVector3D(tmpPoint,1,1,1);
m_myTestPointSet1->InsertPoint(1,tmpPoint);
mitk::FillVector3D(tmpPoint,0.5,0.5,0.5);
m_myTestPointSet2->InsertPoint(0,tmpPoint);
mitk::FillVector3D(tmpPoint,2,2,2);
m_myTestPointSet2->InsertPoint(1,tmpPoint);
//Ground truth values (No logic in tests! Do not change values! :))
const double mean = 1.299038105676658E+00; // from (sqrt(0.75)+sqrt(3))/2;
const double variance = 0.1875; // from ((sqrt(0.75)-mean)*(sqrt(0.75)-mean)+(sqrt(3)-mean)*(sqrt(3)-mean))/2;
const double sd = 4.330127018922193E-01; //from sqrt(variance);
const double rms = 1.369306393762915E+00; //from sqrt(3.75/2);
const double min = 0.86602540378444; //from sqrt(0.75);
const double max = 1.73205080756888; //from sqrt(3);
const double median = 1.29903810567666; //from (min + max)/2;
m_myPointSetDifferenceStatisticsCalculator->SetPointSets( m_myTestPointSet1, m_myTestPointSet2);
MITK_TEST_CONDITION_REQUIRED((m_myPointSetDifferenceStatisticsCalculator->GetNumberOfPoints()==m_myTestPointSet1->GetSize()),".. Testing GetNumberOfPoints");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMean(),mean, 1E-5),".. Testing GetMean");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetSD(),sd,1E-6),".. Testing GetSD");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetVariance(),variance,1E-4),".. Testing GetVariance");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetRMS(),rms,1E-5),".. Testing GetRMS");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMin(),min,1E-6),".. Testing GetMin");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMax(),max,1E-5),".. Testing GetMax");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMedian(),median,1E-5),".. Testing GetMedian");
}
void PointSetDifferenceStatisticsCalculator_PointSetsOfSameSizeWithDifferentPointIDs_ResultsInGroundTruth()
{
Setup();
mitk::Point3D tmpPoint;
//Fill the point sets with simple test data, but different point IDs
mitk::FillVector3D(tmpPoint,1,1,1);
m_myTestPointSet1->InsertPoint(2,tmpPoint);
m_myTestPointSet2->InsertPoint(0,tmpPoint);
mitk::FillVector3D(tmpPoint,3.5,4.9,2.1); //same point in both pointsets
m_myTestPointSet1->InsertPoint(17,tmpPoint);
m_myTestPointSet2->InsertPoint(522,tmpPoint);
m_myPointSetDifferenceStatisticsCalculator->SetPointSets(m_myTestPointSet1, m_myTestPointSet2);
//Compare results to ground truth which is 0.0, because the sets are identical
MITK_TEST_CONDITION_REQUIRED((m_myPointSetDifferenceStatisticsCalculator->GetNumberOfPoints()==m_myTestPointSet1->GetSize()),".. Testing GetNumberOfPoints");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMean(),0.0),".. Testing GetMean");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetSD(),0.0),".. Testing GetSD");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetVariance(),0.0),".. Testing GetVariance");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetRMS(),0.0),".. Testing GetRMS");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMin(),0.0),".. Testing GetMin");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMax(),0.0),".. Testing GetMax");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMedian(),0.0),".. Testing GetMedian");
}
void PointSetDifferenceStatisticsCalculator_TwoPointSetsOfDifferentSize_ThrowsException()
{
Setup();
//One set with 2 points and one set with 1 point
mitk::Point3D tmpPoint;
tmpPoint.Fill(0);
m_myTestPointSet1->InsertPoint(0, tmpPoint);
m_myTestPointSet1->InsertPoint(1, tmpPoint);
m_myTestPointSet2->InsertPoint(0, tmpPoint);
m_myPointSetDifferenceStatisticsCalculator->SetPointSets(m_myTestPointSet1, m_myTestPointSet2);
MITK_TEST_FOR_EXCEPTION(itk::ExceptionObject,m_myPointSetDifferenceStatisticsCalculator->GetMean());
}
void PointSetDifferenceStatisticsCalculator_PointSetWithSizeZero_ThrowsException()
{
Setup();
m_myPointSetDifferenceStatisticsCalculator->SetPointSets(m_myTestPointSet1, m_myTestPointSet2);
MITK_TEST_FOR_EXCEPTION(itk::ExceptionObject,m_myPointSetDifferenceStatisticsCalculator->GetMean());
}
int mitkPointSetDifferenceStatisticsCalculatorTest(int, char* [])
{
MITK_TEST_BEGIN("mitkPointSetDifferenceStatisticsCalculatorTest")
PointSetDifferenceStatisticsCalculator_DefaultConstructor_ResultIsNotNull();
PointSetDifferenceStatisticsCalculator_NonDefaultConstructor_ResultIsNotNull();
PointSetDifferenceStatisticsCalculator_TwoSimplePointSetsOfSizeTwo_ResultsInGroundTruthValues();
PointSetDifferenceStatisticsCalculator_PointSetWithSizeZero_ThrowsException();
PointSetDifferenceStatisticsCalculator_TwoPointSetsOfDifferentSize_ThrowsException();
PointSetDifferenceStatisticsCalculator_PointSetsOfSameSizeWithDifferentPointIDs_ResultsInGroundTruth();
MITK_TEST_END()
}
diff --git a/Modules/ImageStatistics/itkMultiGaussianImageSource.h b/Modules/ImageStatistics/itkMultiGaussianImageSource.h
index c557a6f991..90340f0b7e 100644
--- a/Modules/ImageStatistics/itkMultiGaussianImageSource.h
+++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.h
@@ -1,374 +1,374 @@
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
/*=========================================================================
*
* Portions of this file are subject to the VTK Toolkit Version 3 copyright.
*
* Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
*
* For complete copyright, license and disclaimer of warranty information
* please refer to the NOTICE file at the top of the ITK source tree.
*
*=========================================================================*/
#ifndef __itkMultiGaussianImageSource_h
#define __itkMultiGaussianImageSource_h
#include "itkImageSource.h"
#include "itkNumericTraits.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkImageFileWriter.h"
#include <itkMapContainer.h>
namespace itk
{
/** \class MultiGaussianImageSource
\brief Generate an 3-dimensional multigaussian image.
This class defines an 3-dimensional Image, in which the value at one voxel equals the value of a multigaussian function evaluated at the voxel's coordinates. The multigaussian function is built as a sum of N gaussian function and is defined by the following parameters (\ref Generation-of-a-multigauss-image):
1. CenterX, CenterY, CenterZ - vectors of the size of N determining the expectancy value at the x-, y- and the z-axis. That means: The i-th gaussian bell curve takes its maximal value at the voxel with index [CenterX(i); CenterY(i); Centerz(i)].
2. SigmaX, SigmaY, SigmaZ - vectors of the size of N determining the deviation at the x-, y- and the z-axis. That means: The width of the i-th gaussian bell curve is determined by the deviation in the x-axis, which is SigmaX(i), in the y-axis is SigmaY(i) and in the z-axis is SigmaZ(i).
3. Altitude - vector of the size of N determining the altitude: the i-th gaussian bell curve has a height of Altitude(i).
This class allows by the method CalculateMidpointAndMeanValue() to find a sphere with a specified radius that has a maximal mean value over all sphere with that radius with midpoint inside or on the boundary of the image. Furthermore it can calculate the maximal und minimal pixel intensities and whose indices in the founded sphere.
To serve as a test tool for ImageStatisticsCalculator, esp. the "hotspot search" feature of this class, MultiGaussianImageSource is also able to calculate the position of a sphere that maximizes the mean value of the voxels within the sphere (\ref Algorithm-for-calculating-statistic-in-a-sphere).
\section Generation-of-a-multigauss-image Generation of a multigauss image
A multigauss function consists of the sum of \f$ N \f$ gauss function. The \f$ i \f$-th \n (\f$0 \leq i \leq N \f$) gaussian is described with the following seven parameters (see above):
- \f$ x_0^{(i)} \f$ is the expectancy value in the \f$ x \f$-Axis
- \f$ y_0^{(i)} \f$ is the expectancy value in the \f$ y \f$-Axis
- \f$ z_0^{(i)} \f$ is the expectancy value in the \f$ z \f$-Axis
- \f$ \sigma_x^{(i)} \f$ is the deviation in the \f$ x \f$-Axis
- \f$ \sigma_y^{(i)} \f$ is the deviation in the \f$ y \f$-Axis
- \f$ \sigma_z^{(i)} \f$ is the deviation in the \f$ z \f$-Axis
- \f$ a^{(i)} \f$ is the altitude of the gaussian.
A gauss function has the following form:
\f{eqnarray}{
\nonumber
f^{(i)}(x,y,z) = a^{(i)}
exp \left[ - \left(
\frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} +
\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} +
\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2}
\right) \right].
\f}
A multigauss function has then the form:
\f{align*}{
f_N(x,y,z) =& \sum_{i=0}^{N}f^{(i)}(x,y,z)\\
=&\sum_{0}^{N} a^{(i)}
exp \left[ - \left(
\frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} +
\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} +
\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2}
\right) \right].
\f}
The multigauss function \f$f_N\f$ will be evaluated at each voxel coordinate to become the voxel intensity.
\section Algorithm-for-calculating-statistic-in-a-sphere Algorithm for calculating statistic in a sphere
This section explains how we can find a sphere region which has a maximal mean value over all sphere regions with a fixed radius. Furthermore we want to calculate the maximal and minimal value in the wanted sphere.
To calculate the mean value in a sphere we integrate the gaussians over the whole sphere. The antiderivative is unknown as an explicit function, but we have a value table for the distribution function of the normal distribution \f$ \Phi(x) \f$ for \f$ x \f$ between \f$ -3.99 \f$ and \f$ 3.99 \f$ with step size \f$ 0.01 \f$. The only problem is that we cannot integrate over a spherical region, because we have an 3-dim integral and therefore are the integral limits dependent from each other and we cannot evaluate \f$ \Phi \f$. So we approximate the sphere with cuboids inside the sphere and prisms on the boundary of the sphere. We calculate these cuboids with the octree recursive method: We start by subdividing the wrapping box of the sphere in eight cuboids. Further we subdivide each cuboid in eight cuboids and check for each of them, whether it is inside or outside the sphere or whether it intersects the sphere surface. We save those of them, which are inside the sphere and continue to subdivide the cuboids that intersect the sphere until the recursion breaks. In the last step we take the half of the cuboids on the boundary and this are the prisms. Now we can calculate and sum the integrals over the cuboids and divide through the volume of the body to obtain the mean value.
For each cuboid \f$ Q = [a_1, b_1]\times[a_2, b_2]\times[a_3, b_3] \f$ we apply Fubini's theorem for integrable functions and become for the integral the following:
\f{align*}{
m_k =& \sum_{i=0}^{N} \int_{Q} f^{(i)}(x,y,z)dx\\
=&\sum_{i=0}^{N} a^{(i)} \int_{Q}
exp \left[ - \left(
\frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} +
\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} +
\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2}
\right) \right] dx \\
=& \sum_{i=0}^{N} a^{(i)} \int_{a_1}^{b_1} exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx
\int_{a_2}^{b_2}exp \left[ -\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} \right]dx
\int_{a_3}^{b_3}exp \left[ -\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2} \right]dx.
\f}
So we calculate three one dimensional integrals:
\f{align*}{
\int_{a}^{b} & exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx \\
=&\int_{-\infty}^{b} exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx - \int_{-\infty}^{a} exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx \\
=& \sigma_x^{(i)} \left[\int_{-\infty}^{(a - x_0^{(i)})/ \sigma_x^{(i)}} e^{-\frac{t^2}{2}} dt
- \int_{-\infty}^{(b - x_0^{(i)})/ \sigma_x^{(i)}} e^{-\frac{t^2}{2}}dt \right] \\
=&\sigma_x^{(i)} \sqrt{(\pi)} \left[ \Phi \left( \frac{(a - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) - \Phi \left ( \frac{(b - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) \right].
\f}
and become for the integral over \f$ Q \f$:
\f{align*}{
m_k =& \sum_{i=0}^{N} \sigma_x^{(i)} \sigma_y^{(i)} \sigma_z^{(i)} \pi^{1.5}
\left[ \Phi \left( \frac{(a_1 - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) - \Phi \left ( \frac{(b_1 - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) \right]\times \\
&\left[ \Phi \left( \frac{(a_2 - y_0^{(i)})^2}{\sigma_y^{(i)}} \right) - \Phi \left ( \frac{(b_2 - y_0^{(i)})^2}{\sigma_y^{(i)}} \right) \right]\times
\left[ \Phi \left( \frac{(a_3 - z_0^{(i)})^2}{\sigma_z^{(i)}} \right) - \Phi \left ( \frac{(b_3 - z_0^{(i)})^2}{\sigma_z^{(i)}} \right) \right].
\f}
For the integral over the prism we take the half of the integral over the corresponding cuboid.
Altogether we find the mean value in the sphere as:
\f{align*}{
\left( \sum_{Q_k \text{ Cuboid}} m_k + \sum_{P_l \text{ Prism}} 0.5 m_l \right )/Volume(B),
\f}
where Volume(B) is the volume of the body that approximate the sphere.
Now we know how to calculate the mean value in a sphere for given midpoint and radius. So we assume each voxel in the given image to be the sphere midpoint and we calculate the mean value as described above. If the new mean value is greater than the "maximum-until-now", we take the new value to be the "maximum-until-now". Then we go to the next voxel and make the same calculation and so on. At the same time we save the coordinates of the midpoint voxel.
After we found the midpoint and the maximal mean value, we can calculate the maximum and the minimum in the sphere: we just traverse all the voxels in the region and take the maximum and minimum value and the respective coordinates.
\section Input-and-output Input and output
An example for an input in the command-line is:
\verbatim
mitkMultiGaussianTest C:/temp/outputFile C:/temp/inputFile.xml
\endverbatim
Here is outputFile the name of the gaussian image with extension .nrrd and at the same time the name of the output file with extension .xml, which is the same as the inputFile, only added the calculated mean value, max and min and the corresponding indexes in the statistic tag. Here we see an example for the input and output .xml file:
\verbatim
INPUT:
<testcase>
<testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="2" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
<gaussian centerIndexX="4" centerIndexY="16" centerIndexZ="10" deviationX="7" deviationY="7" deviationZ="7" altitude="200"/>
<gaussian centerIndexX="18" centerIndexY="2" centerIndexZ="10" deviationX="1" deviationY="1" deviationZ="1" altitude="210"/>
</testimage>
<segmentation numberOfLabels="1" hotspotRadiusInMM="6.2035">
<roi label="1" maximumSizeX="20" minimumSizeX="0" maximumSizeY="20" minimumSizeY="0" maximumSizeZ="20" minimumSizeZ="0"/>
</segmentation>
</testcase>
\endverbatim
\verbatim
OUTPUT:
<testcase>
<testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="2" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
<gaussian centerIndexX="4" centerIndexY="16" centerIndexZ="10" deviationX="7" deviationY="7" deviationZ="7" altitude="200"/>
<gaussian centerIndexX="18" centerIndexY="2" centerIndexZ="10" deviationX="1" deviationY="1" deviationZ="1" altitude="210"/>
</testimage>
<segmentation numberOfLabels="1" hotspotRadiusInMM="6.2035">
<roi label="1" maximumSizeX="20" minimumSizeX="0" maximumSizeY="20" minimumSizeY="0" maximumSizeZ="20" minimumSizeZ="0"/>
</segmentation>
<statistic hotspotIndexX="6" hotspotIndexY="13" hotspotIndexZ="10" peak="141.544" mean="141.544" maximumIndexX="4" maximumIndexY="16" maximumIndexZ="10" maximum="200" minimumIndexX="9" minimumIndexY="8" minimumIndexZ="8" minimum="77.4272"/>
</testcase>
\endverbatim
\subsection Parameter-for-the-input Parameter for the input
In the tag \a testimage we describe the image that we generate. Image rows/columns/slices gives the number of rows/columns/slices of the image; \a numberOfGaussians is the number of gauss functions (\f$ N \f$); spacing defines the extend of one voxel for each direction. The parameter \a entireHotSpotInImage determines whether the whole sphere is in the image included (\f$ = 1 \f$) or only the midpoint of the sphere is inside the image.
NOTE: When the \a entireHotSpotInImage \f$ = 0 \f$ it is possible that we find the midpoint of the sphere on the border of the image. In this case we cut the approximation of the sphere, so that we become a body, which is completely inside the image, but not a "sphere" anymore. To that effect is the volume of the body decreased and that could lead to unexpected results.
In the subtag \a gaussian we describe each gauss function as mentioned in the second section.
In the tag \a segmentation we define the radius of the wanted sphere in mm (\a hotspotRadiusInMM ). We can also set the number of labels (\a numberOfLabels ) to be an positive number and this determines the number of regions of interest (ROI). In each ROI we find the sphere with the wanted properties and midpoint inside the ROI, but not necessarily the whole sphere. In the subtag \a roi we set label number and the index coordinates for the borders of the roi.
*/
template< typename TOutputImage >
class ITK_EXPORT MultiGaussianImageSource:public ImageSource< TOutputImage >
{
public:
/** Standard class typedefs. */
typedef MultiGaussianImageSource Self;
typedef ImageSource< TOutputImage > Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Typedef for the output image PixelType. */
typedef typename TOutputImage::PixelType OutputImagePixelType;
/** Typedef to describe the output image region type. */
typedef typename TOutputImage::RegionType OutputImageRegionType;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Basic types from the OutputImageType */
typedef typename TOutputImage::SizeType SizeType;
typedef typename TOutputImage::IndexType IndexType;
typedef typename TOutputImage::SpacingType SpacingType;
typedef typename TOutputImage::PointType PointType;
typedef typename SizeType::SizeValueType SizeValueType;
typedef SizeValueType SizeValueArrayType[TOutputImage::ImageDimension];
typedef typename TOutputImage::SpacingValueType SpacingValueType;
typedef SpacingValueType SpacingValueArrayType[TOutputImage::ImageDimension];
typedef typename TOutputImage::PointValueType PointValueType;
typedef PointValueType PointValueArrayType[TOutputImage::ImageDimension];
typedef typename itk::ImageRegion<3> ::SizeValueType SizeRegionType;
/** Typedef to describe the sphere radius type. */
typedef double RadiusType;
/** Typedef to describe the standard vector type. */
typedef std::vector<double> VectorType;
/** Typedef to describe the itk vector type. */
typedef Vector<double, TOutputImage::ImageDimension> ItkVectorType;
/** Typedef to describe the ImageRegionIteratorWithIndex type. */
typedef ImageRegionIteratorWithIndex<TOutputImage> IteratorType;
- /** Typedef to describe the Poiner type at the output image. */
+ /** Typedef to describe the Pointer type at the output image. */
typedef typename TOutputImage::Pointer ImageType;
typedef MapContainer<unsigned int, PointType> MapContainerPoints;
typedef MapContainer<unsigned int, double> MapContainerRadius;
/** Set/Get size of the output image. */
itkSetMacro(Size, SizeType);
virtual void SetSize(SizeValueArrayType sizeArray);
virtual const SizeValueType * GetSize() const;
/** Set/Get spacing of the output image. */
itkSetMacro(Spacing, SpacingType);
virtual void SetSpacing(SpacingValueArrayType spacingArray);
virtual const SpacingValueType * GetSpacing() const;
- /** Set/Get origin of the output image. This programm works proper only with origin [0.0, 0.0, 0.0] */
+ /** Set/Get origin of the output image. This program works proper only with origin [0.0, 0.0, 0.0] */
itkSetMacro(Origin, PointType);
virtual void SetOrigin(PointValueArrayType originArray);
virtual const PointValueType * GetOrigin() const;
/** Get the number of gaussian functions in the output image. */
virtual unsigned int GetNumberOfGaussians() const;
/** Set the number of gaussian function. */
virtual void SetNumberOfGausssians( unsigned int );
/** Set/Get the radius of the sphere. */
virtual RadiusType GetRadius() const;
virtual void SetRadius( RadiusType radius );
/** Get the maximal mean value in a sphere over all possible spheres with midpoint in the image. */
virtual const OutputImagePixelType GetMaxMeanValue() const;
/** Get the index of the midpoint of a sphere with the maximal mean value.*/
virtual const IndexType GetSphereMidpoint() const;
/** Calculates the value of the multigaussian function at a Point given by its coordinates [x, y, z]. */
virtual double MultiGaussianFunctionValueAtPoint(double , double, double);
/** Adds a multigaussian defined by the parameter: CenterX, CenterY, CenterZ, SigmaX, SigmaY, SigmaZ, Altitude.
All parameters should have the same size, which determinates the number of the gaussian added. */
virtual void AddGaussian( VectorType centerX, VectorType centerY, VectorType centerZ, VectorType sigmaX, VectorType sigmaY, VectorType sigmaZ, VectorType altitude);
/** Calculates and set the index of the midpoint of the sphere with the maximal mean value as well as the mean value. */
virtual void CalculateTheMidpointAndTheMeanValueWithOctree();
/** Calculates and set the index an the value of maximulm and minimum in the wanted sphere. */
virtual void CalculateMaxAndMinInSphere();
/** Get the index in the sphere with maximal value. */
virtual const IndexType GetMaxValueIndexInSphere() const;
/** Get the maximal value in the sphere. */
virtual const OutputImagePixelType GetMaxValueInSphere() const;
/** Get the index in the sphere with minimal value. */
virtual const IndexType GetMinValueIndexInSphere() const;
/** Get the minimal value in the sphere. */
virtual const OutputImagePixelType GetMinValueInSphere() const;
/** Set the region of interest. */
virtual void SetRegionOfInterest(ItkVectorType, ItkVectorType);
/** Write a .mps file to visualise the point in the sphere. */
virtual void WriteXMLToTestTheCuboidInsideTheSphere();
/**This recursive method realise the octree method. It subdivide a cuboid in eight cuboids, when this cuboid crosses the boundary of sphere. If the cuboid is inside the sphere, it calculates the integral. */
virtual void CalculateEdgesInSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius, int level);
/**Calculate and return value of the integral of the gaussian in a cuboid region with the dimension 3: in the x-axis between xMin and xMax and in the y-axis between yMin and yMax and in the z-axis also between zMin and zMax. */
virtual double MultiGaussianFunctionValueAtCuboid(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax);
/** Inseret the midpoints of cuboid in a vector m_Midpoints, so that we can visualise it. */
virtual void InsertPoints( PointType globalCoordinateMidpointCuboid, double cuboidRadius);
- /** Start the octree recursion in eigth directions for the sphere with midpoint globalCoordinateMidpointSphere. */
+ /** Start the octree recursion in eight directions for the sphere with midpoint globalCoordinateMidpointSphere. */
virtual void GenerateCuboidSegmentationInSphere( PointType globalCoordinateMidpointSphere );
- /** Get the the values of the cumulative distribution function of the normal distribution. */
+ /** Get the values of the cumulative distribution function of the normal distribution. */
virtual double FunctionPhi(double value);
/** Check if a cuboid with midpoint globalCoordinateMidpointCuboid and side length sideLength intersect the sphere with midpoint globalCoordinateMidpointSphere boundary. */
virtual unsigned int IntersectTheSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double sideLength);
- /** Set the tabel values of the distribution function of the normal distribution. */
+ /** Set the table values of the distribution function of the normal distribution. */
void SetNormalDistributionValues();
/** Set the minimum possible pixel value. By default, it is
* NumericTraits<TOutputImage::PixelType>::min(). */
itkSetClampMacro( Min, OutputImagePixelType,
NumericTraits< OutputImagePixelType >::NonpositiveMin(),
NumericTraits< OutputImagePixelType >::max() );
/** Check if a index is inside the image*/
bool IsInImage(IndexType index);
/** Get the minimum possible pixel value. */
itkGetConstMacro(Min, OutputImagePixelType);
/** Set the maximum possible pixel value. By default, it is
* NumericTraits<TOutputImage::PixelType>::max(). */
itkSetClampMacro( Max, OutputImagePixelType,
NumericTraits< OutputImagePixelType >::NonpositiveMin(),
NumericTraits< OutputImagePixelType >::max() );
/** Get the maximum possible pixel value. */
itkGetConstMacro(Max, OutputImagePixelType);
protected:
MultiGaussianImageSource();
~MultiGaussianImageSource() override;
void PrintSelf(std::ostream & os, Indent indent) const override;
void GenerateData() override;
void GenerateOutputInformation() override;
private:
MultiGaussianImageSource(const MultiGaussianImageSource &); //purposely not implemented
void operator=(const MultiGaussianImageSource &); //purposely not implemented
SizeType m_Size; //size of the output image
SpacingType m_Spacing; //spacing
PointType m_Origin; //origin
OutputImagePixelType m_MaxValueInSphere; //maximal value in the wanted sphere
IndexType m_MaxValueIndexInSphere; //index of the maximal value in the wanted sphere
OutputImagePixelType m_MinValueInSphere; //minimal value in the wanted sphere
IndexType m_MinValueIndexInSphere; //index of the minimal value in the wanted sphere
unsigned int m_NumberOfGaussians; //number of Gaussians
RadiusType m_Radius; //radius of the sphere
unsigned int m_RadiusStepNumber; //number of steps to traverse the sphere radius
OutputImagePixelType m_MeanValue; //mean value in the wanted sphere
OutputImagePixelType m_ValueAtMidpoint; //value at the midpoint of the wanted sphere
IndexType m_SphereMidpoint; //midpoint of the wanted sphere
VectorType m_SigmaX; //deviation in the x-axis
VectorType m_SigmaY; //deviation in the y-axis
VectorType m_SigmaZ; //deviation in the z-axis
VectorType m_CenterX; //x-coordinate of the mean value of Gaussians
VectorType m_CenterY; //y-coordinate of the mean value of Gaussians
VectorType m_CenterZ; //z-coordinate of the mean value of Gaussians
VectorType m_Altitude; //amplitude
ItkVectorType m_RegionOfInterestMax; //maximal values for the coordinates in the region of interest
ItkVectorType m_RegionOfInterestMin; //minimal values for the coordinates in the region of interest
typename TOutputImage::PixelType m_Min; //minimum possible value
typename TOutputImage::PixelType m_Max; //maximum possible value
- PointType m_GlobalCoordinate; //physical coordiante of the sphere midpoint
+ PointType m_GlobalCoordinate; //physical coordinate of the sphere midpoint
bool m_WriteMPS; //1 = write a MPS File to visualise the cuboid midpoints of one approximation of the sphere
MapContainerPoints m_Midpoints; //the midpoints of the cuboids
MapContainerRadius m_RadiusCuboid; //the radius ( = 0.5 * side length) of the cuboids (in the same order as the midpoints in m_Midpoints)
double m_Volume; //the volume of the body, that approximize the sphere
double m_NormalDistValues [410];//normal distribution values
double m_meanValueTemp; //= m_Volume * meanValue in each sphere
// The following variables are deprecated, and provided here just for
// backward compatibility. It use is discouraged.
mutable PointValueArrayType m_OriginArray;
mutable SpacingValueArrayType m_SpacingArray;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkMultiGaussianImageSource.hxx"
#endif
#endif
diff --git a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
index 03086b02e9..bd5fc55cb2 100644
--- a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
+++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
@@ -1,998 +1,998 @@
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
/*=========================================================================
*
* Portions of this file are subject to the VTK Toolkit Version 3 copyright.
*
* Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
*
* For complete copyright, license and disclaimer of warranty information
* please refer to the NOTICE file at the top of the ITK source tree.
*
*=========================================================================*/
#ifndef __itkMultiGaussianImageSource_hxx
#define __itkMultiGaussianImageSource_hxx
#include <iostream>
#include <fstream>
#include <ctime>
#include "itkMultiGaussianImageSource.h"
#include "itkImageRegionIterator.h"
#include "itkObjectFactory.h"
#include "itkProgressReporter.h"
#include "itkDOMNodeXMLWriter.h"
#include <cstdlib>
namespace itk
{
/**
*
*/
template< class TOutputImage >
MultiGaussianImageSource< TOutputImage >
::MultiGaussianImageSource()
{
//Initial image is 100 wide in each direction.
for ( unsigned int i = 0; i < TOutputImage::GetImageDimension(); i++ )
{
m_Size[i] = 100;
m_Spacing[i] = 1.0;
m_Origin[i] = 0.0;
m_SphereMidpoint[i] = 0;
}
m_NumberOfGaussians = 0;
m_Radius = 1;
m_RadiusStepNumber = 5;
m_MeanValue = 0;
m_Min = NumericTraits< OutputImagePixelType >::NonpositiveMin();
m_Max = NumericTraits< OutputImagePixelType >::max();
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
MultiGaussianImageSource< TOutputImage >
::~MultiGaussianImageSource()
{}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetSize(SizeValueArrayType sizeArray)
{
const unsigned int count = TOutputImage::ImageDimension;
unsigned int i;
for ( i = 0; i < count; i++ )
{
if ( sizeArray[i] != this->m_Size[i] )
{
break;
}
}
if ( i < count )
{
this->Modified();
for ( i = 0; i < count; i++ )
{
this->m_Size[i] = sizeArray[i];
}
}
}
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::SizeValueType *
MultiGaussianImageSource< TOutputImage >
::GetSize() const
{
return this->m_Size.GetSize();
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetSpacing(SpacingValueArrayType spacingArray)
{
const unsigned int count = TOutputImage::ImageDimension;
unsigned int i;
for ( i = 0; i < count; i++ )
{
if ( spacingArray[i] != this->m_Spacing[i] )
{
break;
}
}
if ( i < count )
{
this->Modified();
for ( i = 0; i < count; i++ )
{
this->m_Spacing[i] = spacingArray[i];
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetOrigin(PointValueArrayType originArray)
{
const unsigned int count = TOutputImage::ImageDimension;
unsigned int i;
for ( i = 0; i < count; i++ )
{
if ( originArray[i] != this->m_Origin[i] )
{
break;
}
}
if ( i < count )
{
this->Modified();
for ( i = 0; i < count; i++ )
{
this->m_Origin[i] = originArray[i];
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::PointValueType *
MultiGaussianImageSource< TOutputImage >
::GetOrigin() const
{
for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
{
this->m_OriginArray[i] = this->m_Origin[i];
}
return this->m_OriginArray;
}
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::SpacingValueType *
MultiGaussianImageSource< TOutputImage >
::GetSpacing() const
{
for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
{
this->m_SpacingArray[i] = this->m_Spacing[i];
}
return this->m_SpacingArray;
}
//-----------------------------------------------------------------------------------------------------------------------
/**
*
*/
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Max: "
<< static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Max )
<< std::endl;
os << indent << "Min: "
<< static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Min )
<< std::endl;
os << indent << "Origin: [";
unsigned int ii = 0;
while( ii < TOutputImage::ImageDimension - 1 )
{
os << m_Origin[ii] << ", ";
++ii;
}
os << m_Origin[ii] << "]" << std::endl;
os << indent << "Spacing: [";
ii = 0;
while( ii < TOutputImage::ImageDimension - 1 )
{
os << m_Spacing[ii] << ", ";
++ii;
}
os << m_Spacing[ii] << "]" << std::endl;
os << indent << "Size: [";
ii = 0;
while( ii < TOutputImage::ImageDimension - 1 )
{
os << m_Size[ii] << ", ";
++ii;
}
os << m_Size[ii] << "]" << std::endl;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
unsigned int
MultiGaussianImageSource< TOutputImage >
::GetNumberOfGaussians() const
{
return this->m_NumberOfGaussians;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
typename MultiGaussianImageSource< TOutputImage >::RadiusType
MultiGaussianImageSource< TOutputImage >
::GetRadius() const
{
return this->m_Radius;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetRadius( RadiusType radius )
{
this->m_Radius = radius;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetNumberOfGausssians( unsigned int n )
{
this->m_NumberOfGaussians = n;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetRegionOfInterest( ItkVectorType roiMin, ItkVectorType roiMax )
{
m_RegionOfInterestMax = roiMax;
m_RegionOfInterestMin = roiMin;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType
MultiGaussianImageSource< TOutputImage >
::GetMaxMeanValue() const
{
return m_MeanValue;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType
MultiGaussianImageSource< TOutputImage >
::GetMaxValueInSphere() const
{
return m_MaxValueInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::IndexType
MultiGaussianImageSource< TOutputImage >
::GetMaxValueIndexInSphere() const
{
return m_MaxValueIndexInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType
MultiGaussianImageSource< TOutputImage >
::GetMinValueInSphere() const
{
return m_MinValueInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::IndexType
MultiGaussianImageSource< TOutputImage >
::GetMinValueIndexInSphere() const
{
return m_MinValueIndexInSphere;
}
//-----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
const typename MultiGaussianImageSource< TOutputImage >::IndexType
MultiGaussianImageSource< TOutputImage >
::GetSphereMidpoint() const
{
return m_SphereMidpoint;
}
//-----------------------------------------------------------------------------------------------------------------------
/* Calculate and return value of the integral of the gaussian in a cuboid region with the dimension 3: in the x-axis between xMin and xMax and in the y-axis between yMin and yMax and in the z-axis also between zMin and zMax. */
template< class TOutputImage >
double
MultiGaussianImageSource< TOutputImage >
::MultiGaussianFunctionValueAtCuboid(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax)
{
double mean = 0;
double summand0, summand1, summand2, value, factor;
for(unsigned int n = 0; n < m_NumberOfGaussians; ++n)
{
summand0 = FunctionPhi((xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi((xMin - m_CenterX[n]) / m_SigmaX[n] );
summand1 = FunctionPhi((yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi((yMin - m_CenterY[n]) / m_SigmaY[n] );
summand2 = FunctionPhi((zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi((zMin - m_CenterZ[n]) / m_SigmaZ[n] );
value = summand0 * summand1 * summand2;
factor = (m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow(2.0 * itk::Math::pi, 1.5 );
mean = mean + factor * value * m_Altitude[n];
}
return mean;
}
//---------------------------------------------------------------------------------------------------------------------
/*
Returns the linear interpolation of the values of the standard normal distribution function. This values could be seen in the vector m_NormalDistValues.
*/
template< class TOutputImage >
double
MultiGaussianImageSource< TOutputImage >
::FunctionPhi(double value)
{
double phiAtValue;
//linear interpolation between the values
int indexValue = static_cast<int>( 100 * value);
if( indexValue > 409 )
{
return phiAtValue = 1.0;
}
else if( indexValue < -409 )
{
return phiAtValue = 0.0;
}
else if( indexValue == 409 )
{
return phiAtValue = m_NormalDistValues[409];
}
else if( indexValue == -409 )
{
return phiAtValue = 1.0 - m_NormalDistValues[409];
}
else
{
bool negative = false;
if (indexValue < 0.0)
{
negative = true;
value = -value;
}
int indexUp = static_cast<int>( 100 * value) + 1;
int indexDown = static_cast<int>( 100 * value);
double alpha = 100.0 * value - static_cast<double>(indexDown);
phiAtValue = (1.0 - alpha) * m_NormalDistValues[indexDown] + alpha * m_NormalDistValues[indexUp] ;
if(negative)
{
phiAtValue = 1.0 - phiAtValue;
}
return phiAtValue;
}
}
//----------------------------------------------------------------------------------------------------------------------
/*
Set the midpoint of the cuboid in a vector m_Midpoints. This cuboids discretise the sphere with the octree method.
Set the radius of the cuboid ( = 0.5 * length of the side of the cuboid ) in the vector m_RadiusCuboid.
*/
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::InsertPoints( PointType globalCoordinateMidpointCuboid, double cuboidRadius)
{
typename MapContainerPoints::ElementIdentifier id = m_Midpoints.Size();
m_Midpoints.InsertElement(id, globalCoordinateMidpointCuboid);
m_RadiusCuboid.InsertElement(id, cuboidRadius);
}
//----------------------------------------------------------------------------------------------------------------------
/* This recursive method realise the octree method. It subdivide a cuboid in eight cuboids, when this cuboid crosses the boundary of sphere. If the cuboid is inside the sphere, it calculates the integral and, if uncommented, insert the midpoint of the cuboid in the m_Midpoints vector.
*/
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::CalculateEdgesInSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius, int level )
{
double xMin, xMax, yMin, yMax, zMin, zMax;
double cuboidRadiusRecursion = cuboidRadius;
PointType newMidpoint;
int intersect = IntersectTheSphere( globalCoordinateMidpointCuboid, globalCoordinateMidpointSphere, cuboidRadiusRecursion);
if( intersect == 1 )
{
if (level < 4)
{
// cuboid intersect the sphere -> call CalculateEdgesInSphere (this method) for the subdivided cuboid
cuboidRadiusRecursion = cuboidRadiusRecursion / 2.0;
for(int i = -1; i < 2; i+=2)
{
for(int k = -1; k < 2; k+=2)
{
for(int j = -1; j < 2; j+=2)
{
newMidpoint[0] = globalCoordinateMidpointCuboid[0] + static_cast<double>(i) * cuboidRadiusRecursion;
newMidpoint[1] = globalCoordinateMidpointCuboid[1] + static_cast<double>(k) * cuboidRadiusRecursion;
newMidpoint[2] = globalCoordinateMidpointCuboid[2] + static_cast<double>(j) * cuboidRadiusRecursion;
this->CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadiusRecursion, level + 1 );
}
}
}
}
// last step of recursion -> on the boundary
else
{
// Calculate the integral and take the half of it (because we are on the boundary)
xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius;
xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius;
yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius;
yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius;
zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius;
zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius;
// size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary
// yz Plane
bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0];
// xz Plane
bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1];
// xy Plane
bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2];
//check if the boundary of the integral is inside the image
if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection)
{
//double temp = this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5;
m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5;
m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0) / 2.0;
}
}
}
else if(intersect == 2)
{
// cuboid in the sphere
// To insert the midpoint and the radius of the cuboid in a vector, so that we can visualise the midpoints, uncomment the next line and the line WriteXMLToTestTheCuboidInsideTheSphere();
// InsertPoints(globalCoordinateMidpointCuboid, cuboidRadius);
// Calculate the integral boundary
xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius;
xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius;
yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius;
yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius;
zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius;
zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius;
// size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary
// yz Plane
// bool yzPlaneAtOriginCrossXSection = xMin <= m_Origin[0] && xMax >= m_Origin[0];
// bool yzPlaneAtImageBorderCrossXSection = xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0];
bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0];
// xz Plane
// bool xzPlaneAtOriginCrossYSection = yMin <= m_Origin[1] && yMax >= m_Origin[1];
// bool xzPlaneAtImageBorderCrossYSection = yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1];
bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1];
// xy Plane
// bool xyPlaneAtOriginCrossZSection = zMin <= m_Origin[2] && zMax >= m_Origin[2];
// bool xyPlaneAtImageBorderCrossZSection = zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2];
bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2];
if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection)
{
m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax );
m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0);
}
// check if the boundary of the image intersect the cuboid and if yes, change the limits of the cuboid to be only inside the image; therefor we cut the sphere and neglect the part of it outside the image
else
/*
if( // one plane crosses the cuboid
( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) )
|| // two plane cross the cuboid (on the image edges possible)
( (yzPlaneAtOriginCrossXSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) ||
( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) ||
(yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection)) ||
(yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) )
)
*/
{
// x-Axis
if(xMin <= m_Origin[0] && xMax >= m_Origin[0])
{
xMin = m_Origin[0];
}else if(xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0])
{
xMax = m_Size[0] * m_Spacing[0];
}
// y-Axis
if(yMin <= m_Origin[1] && yMax >= m_Origin[1])
{
yMin = m_Origin[1];
}else if(yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1])
{
yMax = m_Size[1] * m_Spacing[1];
}
// z-Axis
if(zMin <= m_Origin[2] && zMax >= m_Origin[2])
{
zMin = m_Origin[2];
}else if(zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2])
{
zMax = m_Size[2] * m_Spacing[2];
}
m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax );
m_Volume = m_Volume + (xMax - xMin) * (yMax - yMin) * (zMax - zMin) ;
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
- /* Start the octree recursion in eigth directions for the sphere with midpoint globalCoordinateMidpointSphere and, if uncommented, write this in a file, so that we can visualise it. */
+ /* Start the octree recursion in eight directions for the sphere with midpoint globalCoordinateMidpointSphere and, if uncommented, write this in a file, so that we can visualise it. */
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::GenerateCuboidSegmentationInSphere(PointType globalCoordinateMidpointSphere)
{
double cuboidRadius = m_Radius * 0.5;
PointType newMidpoint;
for(int i = -1; i < 2; i+=2)
{
for(int k = -1; k < 2; k+=2)
{
for(int j = -1; j < 2; j+=2)
{
newMidpoint[0] = globalCoordinateMidpointSphere[0] + static_cast<double>(i) * cuboidRadius;
newMidpoint[1] = globalCoordinateMidpointSphere[1] + static_cast<double>(k) * cuboidRadius;
newMidpoint[2] = globalCoordinateMidpointSphere[2] + static_cast<double>(j) * cuboidRadius;
CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadius, 0);
}
}
}
if(m_WriteMPS)
{
m_WriteMPS = 0;
// uncomment to write an .mps file to visualise the midpoints
// std::cout << "Wrote .xml to visualise the midpoints." << std::endl;
// WriteXMLToTestTheCuboidInsideTheSphere();
}
}
//----------------------------------------------------------------------------------------------------------------------
/* This class allows by the method CalculateTheMidpointAndTheMeanValueWithOctree() to find a sphere with a specified radius that has a maximal mean value over all sphere with that radius with midpoint inside or at the boundary of the image. We approximaze the sphere with the octree recursiv method.
CalculateTheMidpointAndTheMeanValueWithOctree works as follows:
1. The for-loops traverse the region of interest and assume the current point to be the wanted sphere midpoint.
2. Calculate the mean value for that sphere.
3. Compare with the until-now-found-maximum and take the bigger one. */
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::CalculateTheMidpointAndTheMeanValueWithOctree()
{
m_MeanValue = 0.0;
double meanValueTemp;
m_WriteMPS = 1;
PointType globalCoordinateMidpointSphere;
IndexType index;
OutputImageRegionType regionOfInterest;
IndexType indexR;
indexR.SetElement( 0, m_RegionOfInterestMin[0] );
indexR.SetElement( 1, m_RegionOfInterestMin[1] );
indexR.SetElement( 2, m_RegionOfInterestMin[2] );
regionOfInterest.SetIndex(indexR);
SizeType sizeROI;
sizeROI.SetElement( 0, m_RegionOfInterestMax[0] - m_RegionOfInterestMin[0] + 1);
sizeROI.SetElement( 1, m_RegionOfInterestMax[1] - m_RegionOfInterestMin[1] + 1);
sizeROI.SetElement( 2, m_RegionOfInterestMax[2] - m_RegionOfInterestMin[2] + 1);
regionOfInterest.SetSize(sizeROI);
typename TOutputImage::Pointer image = this->GetOutput(0);
IteratorType regionOfInterestIterator(image, regionOfInterest);
for(regionOfInterestIterator.GoToBegin(); !regionOfInterestIterator.IsAtEnd(); ++regionOfInterestIterator)
{
index = regionOfInterestIterator.GetIndex();
image->TransformIndexToPhysicalPoint(index, globalCoordinateMidpointSphere);
m_Volume = 0.0;
m_meanValueTemp = 0.0;
this->GenerateCuboidSegmentationInSphere(globalCoordinateMidpointSphere);
meanValueTemp = m_meanValueTemp / m_Volume;
// std::cout << "index: " << index <<" meanvalue: " << meanValueTemp << std::endl;
if(meanValueTemp > m_MeanValue)
{
m_GlobalCoordinate = globalCoordinateMidpointSphere;
m_MeanValue = meanValueTemp;
m_SphereMidpoint = index;
}
}
}
//----------------------------------------------------------------------------------------------------------------------
/*
Check if a cuboid intersect the sphere boundary. Returns 2, if the cuboid is inside the sphere; returns 1, if the cuboid intersects the sphere boundary and 0, if the cuboid is out of the sphere.
*/
template< class TOutputImage >
unsigned int
MultiGaussianImageSource< TOutputImage >
::IntersectTheSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius)
{
unsigned int intersect = 1;
PointType cuboidEdge;
int count = 0;
for(int i = -1; i < 2; i+=2)
{
for(int k = -1; k < 2; k+=2)
{
for(int j = -1; j < 2; j+=2)
{
cuboidEdge[0] = globalCoordinateMidpointCuboid[0] + static_cast<double>(i) * cuboidRadius;
cuboidEdge[1] = globalCoordinateMidpointCuboid[1] + static_cast<double>(k) * cuboidRadius;
cuboidEdge[2] = globalCoordinateMidpointCuboid[2] + static_cast<double>(j) * cuboidRadius;
if (globalCoordinateMidpointSphere.SquaredEuclideanDistanceTo(cuboidEdge) <= m_Radius * m_Radius)
{
++count;
}
}
}
}
if ( count == 0 )
{
// cuboid not in the sphere
intersect = 0;
}
if (count == 8 )
{
// cuboid in the sphere
intersect = 2;
}
return intersect;
}
//----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
double
MultiGaussianImageSource< TOutputImage >
::MultiGaussianFunctionValueAtPoint(double x, double y, double z)
{
- //this claculate the mean value in the voxel
+ //this calculates the mean value in the voxel
//integrate over the voxel with midpoint [x, y, z]
double summand0, summand1, summand2/*, power*/, value = 0.0, factor;
double xMin, xMax, yMin, yMax, zMin, zMax, mean;
mean = 0.0;
// the for-loop represent the sum of the gaussian function
xMin = x - m_Spacing[0] / 2.0;
xMax = x + m_Spacing[0] / 2.0;
yMin = y - m_Spacing[1] / 2.0;
yMax = y + m_Spacing[1] / 2.0;
zMin = z - m_Spacing[2] / 2.0;
zMax = z + m_Spacing[2] / 2.0;
for( unsigned int n = 0; n < m_NumberOfGaussians; ++n )
{
summand0 = FunctionPhi( (xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi( (xMin - m_CenterX[n]) / m_SigmaX[n] );
summand1 = FunctionPhi( (yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi( (yMin - m_CenterY[n]) / m_SigmaY[n] );
summand2 = FunctionPhi( (zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi( (zMin - m_CenterZ[n]) / m_SigmaZ[n] );
value = summand0 * summand1 * summand2;
factor = ( m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow( 2.0 * itk::Math::pi, 1.5 );
mean = mean + factor * value * m_Altitude[n];
}
value = mean / (m_Spacing[0] * m_Spacing[1] * m_Spacing[2] );
/*
//this calculate the value of the gaussian at the midpoint of the voxel:
double summand0, summand1, summand2, power, value = 0.0;
// the for-loop represent the sum of the gaussian function
for(unsigned int n =0; n < m_NumberOfGaussians; ++n)
{
summand0 = ( x - m_CenterX[n] ) / m_SigmaX[n];
summand1 = ( y - m_CenterY[n] ) / m_SigmaY[n];
summand2 = ( z - m_CenterZ[n] ) / m_SigmaZ[n];
power = summand0 * summand0 + summand1 * summand1 + summand2 * summand2;
value = value + m_Altitude[n] * pow(itk::Math::e, -0.5 * power);
}
*/
// std::cout << "X: " << xMin << " " << x << " "<< xMax << " Y: "<< yMin << " " << y << " " << yMax << " Z: "<< zMin << " "<< z << " " << zMax << " value: " << value << std::endl;
return value;
}
//----------------------------------------------------------------------------------------------------------------------
template< class TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::AddGaussian( VectorType x, VectorType y, VectorType z, VectorType sx, VectorType sy, VectorType sz, VectorType altitude)
{
for(unsigned int i = 0; i < x.size(); ++i)
{
m_CenterX.push_back( x[i] );
m_CenterY.push_back( y[i] );
m_CenterZ.push_back( z[i] );
m_SigmaX.push_back( sx[i] );
m_SigmaY.push_back( sy[i] );
m_SigmaZ.push_back( sz[i] );
m_Altitude.push_back(altitude[i]);
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::GenerateOutputInformation()
{
TOutputImage *output;
IndexType index;
index.Fill(0);
output = this->GetOutput(0);
typename TOutputImage::RegionType largestPossibleRegion;
largestPossibleRegion.SetSize(this->m_Size);
largestPossibleRegion.SetIndex(index);
output->SetLargestPossibleRegion(largestPossibleRegion);
output->SetSpacing(m_Spacing);
output->SetOrigin(m_Origin);
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::GenerateData()
{
itkDebugMacro(<< "Generating a image of scalars ");
double valueReal;
IndexType index;
typename TOutputImage::Pointer image = this->GetOutput(0);
image = this->GetOutput(0);
image->SetBufferedRegion( image->GetRequestedRegion() );
image->Allocate();
IteratorType imageIt(image, image->GetLargestPossibleRegion());
PointType globalCoordinate;
this->SetNormalDistributionValues();
for(imageIt.GoToBegin(); !imageIt.IsAtEnd(); ++imageIt)
{
valueReal = 0.0;
index = imageIt.GetIndex();
image->TransformIndexToPhysicalPoint(imageIt.GetIndex(), globalCoordinate);
valueReal = MultiGaussianFunctionValueAtPoint(globalCoordinate[0], globalCoordinate[1] ,globalCoordinate[2]);
imageIt.Set(valueReal);
}
}
//-----------------------------------------------------------------------------------------------------------------------
/*This method is used to write a .mps file, so that we can visualize the midpoints of the approximated sphere as a scatterplot (for example with MITK Workbench).
*/
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::WriteXMLToTestTheCuboidInsideTheSphere()
{
std::stringstream ss;
int numberSummand = 1.0;
//write an .mps test file
itk::DOMNodeXMLWriter::Pointer xmlWriter;
typedef itk::DOMNode::Pointer DOMNodeType;
DOMNodeType domXML, domPointSetFile, domFileVersion, domPointSet, domPoint, domId, domX, domY, domZ;
xmlWriter = itk::DOMNodeXMLWriter::New();
domXML = itk::DOMNode::New();
domXML->SetName("?xml");
domPointSetFile = itk::DOMNode::New();
domPointSetFile->SetName("point_set_file");
//domFileVersion = itk::DOMNode::New();
//domFileVersion->SetName("file_version");
domPointSet = itk::DOMNode::New();
domPointSet->SetName("point_set");
ss.str("");
ss << 1.0;
domXML->SetAttribute("version", ss.str());
domXML->AddChildAtBegin(domPointSetFile);
//domPointSetFile -> AddChildAtBegin(domFileVersion);
domPointSetFile -> AddChildAtBegin(domPointSet);
unsigned int cap = m_Midpoints.Size();
for(unsigned int iter = 0 ; iter < cap; ++iter)
{
domPoint = itk::DOMNode::New();
domPoint->SetName("point");
domX = itk::DOMNode::New();
domX->SetName("x");
domY = itk::DOMNode::New();
domY->SetName("y");
domZ = itk::DOMNode::New();
domZ->SetName("z");
domId = itk::DOMNode::New();
domId->SetName("id");
ss.str("");
ss << numberSummand;
domId->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domId);
double scaleFactor = 10.0;
PointType point = m_Midpoints.GetElement( numberSummand - 1 );
ss.str("");
ss << point[0] * scaleFactor;
domX->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domX);
ss.str("");
ss << point[1] * scaleFactor;
domY->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domY);
ss.str("");
ss << point[2] * scaleFactor;
domZ->AddTextChildAtBegin(ss.str());
domPoint -> AddChildAtEnd(domZ);
domPointSet -> AddChildAtEnd(domPoint);
numberSummand += 1.0;
}
// .mps (Data)
ss.str("");
ss << "C:/temp/CuboidsInTheSphere.mps";
std::string name = ss.str();
char * fileNamePointer = (char*) name.c_str();
xmlWriter->SetFileName( fileNamePointer);
xmlWriter->SetInput( domXML );
xmlWriter->Update();
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::CalculateMaxAndMinInSphere()
{
IndexType index;
typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType value;
m_MaxValueInSphere = std::numeric_limits<OutputImagePixelType>::min();
m_MinValueInSphere = std::numeric_limits<OutputImagePixelType>::max();
int radInt, sizeRegion;
OutputImageRegionType cuboidRegion;
IndexType indexR;
SizeType sizeR;
int indexRegion, originAsIndex;
for( unsigned int i = 0; i < 3; ++i )
{
radInt = static_cast<int>(m_Radius/m_Spacing[i]);
indexRegion = m_SphereMidpoint[i] - radInt;
originAsIndex = static_cast<int>(m_Origin[i]/m_Spacing[i]);
if( originAsIndex > indexRegion )
{
indexR.SetElement(i, originAsIndex );
}
else
{
indexR.SetElement(i, indexRegion );
}
sizeRegion = 2 *radInt + 1;
int sizeOutputImage = m_Size[i];
if( (indexR[i] + sizeRegion) > (originAsIndex + sizeOutputImage) )
{
std::cout << "Not the entire sphere is in the image!" << std::endl;
sizeR.SetElement(i, m_Size[i] - indexRegion );
}
else
{
sizeR.SetElement(i, sizeRegion );
}
}
cuboidRegion.SetIndex(indexR);
cuboidRegion.SetSize(sizeR);
typename TOutputImage::Pointer image = this->GetOutput(0);
IteratorType cuboidRegionOfInterestIterator(image, cuboidRegion);
PointType globalCoordinate;
for(cuboidRegionOfInterestIterator.GoToBegin(); !cuboidRegionOfInterestIterator.IsAtEnd(); ++cuboidRegionOfInterestIterator)
{
index = cuboidRegionOfInterestIterator.GetIndex();
if(IsInImage(index))
{
image->TransformIndexToPhysicalPoint(cuboidRegionOfInterestIterator.GetIndex(), globalCoordinate);
if( m_GlobalCoordinate.EuclideanDistanceTo(globalCoordinate) <= m_Radius )
{
value = cuboidRegionOfInterestIterator.Get();
if(m_MaxValueInSphere < value)
{
m_MaxValueInSphere = value;
m_MaxValueIndexInSphere = index;
}
if(m_MinValueInSphere > value)
{
m_MinValueInSphere = value;
m_MinValueIndexInSphere = index;
}
}
}
}
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
bool
MultiGaussianImageSource< TOutputImage >
::IsInImage(IndexType index)
{
bool isInImage = true;
int originAsIndex;
for( unsigned int i = 0; i < 3; ++i )
{
originAsIndex = static_cast<int>(m_Origin[i]/m_Spacing[i]);
int sizeOfOutputImage = m_Size[i];
if( index[i] < originAsIndex || index[i] > (originAsIndex + sizeOfOutputImage) )
return false;
}
return isInImage;
}
//-----------------------------------------------------------------------------------------------------------------------
template< typename TOutputImage >
void
MultiGaussianImageSource< TOutputImage >
::SetNormalDistributionValues()
{
double temp[410] = { 0.5 , 0.50399 , 0.50798, 0.51197, 0.51595, 0.51994, 0.52392, 0.5279, 0.53188, 0.53586, 0.53983, 0.5438, 0.54776, 0.55172, 0.55567, 0.55962, 0.56356, 0.56749, 0.57142, 0.57535, 0.57926, 0.58317, 0.58706, 0.59095, 0.59483 , 0.59871, 0.60257, 0.60642, 0.61026, 0.61409, 0.61791, 0.62172, 0.62552, 0.6293, 0.63307, 0.63683, 0.64058, 0.64431, 0.64803, 0.65173, 0.65542, 0.6591, 0.66276, 0.6664, 0.67003, 0.67364, 0.67724, 0.68082, 0.68439, 0.68793, 0.69146, 0.69497, 0.69847, 0.70194, 0.7054, 0.70884, 0.71226, 0.71566, 0.71904, 0.7224, 0.72575, 0.72907, 0.73237, 0.73565, 0.73891, 0.74215, 0.74537, 0.74857, 0.75175, 0.7549, 0.75804, 0.76115, 0.76424, 0.7673, 0.77035, 0.77337, 0.77637, 0.77935, 0.7823, 0.78524, 0.78814, 0.79103, 0.79389, 0.79673, 0.79955, 0.80234, 0.80511, 0.80785, 0.81057, 0.81327, 0.81594, 0.81859, 0.82121, 0.82381, 0.82639, 0.82894, 0.83147, 0.83398, 0.83646, 0.83891, 0.84134, 0.84375, 0.84614, 0.84849, 0.85083, 0.85314, 0.85543, 0.85769, 0.85993, 0.86214, 0.86433, 0.8665, 0.86864, 0.87076, 0.87286, 0.87493, 0.87698, 0.879, 0.881, 0.88298, 0.88493, 0.88686, 0.88877, 0.89065, 0.89251, 0.89435, 0.89617, 0.89796, 0.89973, 0.90147, 0.9032, 0.9049, 0.90658, 0.90824, 0.90988, 0.91149, 0.91309, 0.91466, 0.91621, 0.91774, 0.91924, 0.92073, 0.9222, 0.92364, 0.92507, 0.92647, 0.92785, 0.92922, 0.93056, 0.93189, 0.93319, 0.93448, 0.93574, 0.93699, 0.93822, 0.93943, 0.94062, 0.94179, 0.94295, 0.94408, 0.9452, 0.9463, 0.94738, 0.94845, 0.9495, 0.95053, 0.95154, 0.95254, 0.95352, 0.95449, 0.95543, 0.95637, 0.95728, 0.95818, 0.95907, 0.95994, 0.9608, 0.96164, 0.96246, 0.96327, 0.96407, 0.96485, 0.96562, 0.96638, 0.96712, 0.96784, 0.96856, 0.96926, 0.96995, 0.97062, 0.97128, 0.97193, 0.97257, 0.9732, 0.97381, 0.97441, 0.975, 0.97558, 0.97615, 0.9767, 0.97725, 0.97778, 0.97831, 0.97882, 0.97932, 0.97982, 0.9803, 0.98077, 0.98124, 0.98169, 0.98214, 0.98257, 0.983, 0.98341, 0.98382, 0.98422, 0.98461, 0.985, 0.98537, 0.98574, 0.9861, 0.98645, 0.98679, 0.98713, 0.98745, 0.98778, 0.98809, 0.9884, 0.9887, 0.98899, 0.98928, 0.98956, 0.98983, 0.9901, 0.99036, 0.99061, 0.99086, 0.99111, 0.99134, 0.99158, 0.9918, 0.99202, 0.99224, 0.99245, 0.99266, 0.99286, 0.99305, 0.99324, 0.99343, 0.99361, 0.99379, 0.99396, 0.99413, 0.9943, 0.99446, 0.99461, 0.99477, 0.99492, 0.99506, 0.9952, 0.99534, 0.99547, 0.9956, 0.99573, 0.99585, 0.99598, 0.99609, 0.99621, 0.99632, 0.99643, 0.99653, 0.99664, 0.99674, 0.99683, 0.99693, 0.99702, 0.99711, 0.9972, 0.99728, 0.99736, 0.99744, 0.99752, 0.9976, 0.99767, 0.99774, 0.99781, 0.99788, 0.99795, 0.99801, 0.99807, 0.99813, 0.99819, 0.99825, 0.99831, 0.99836, 0.99841, 0.99846, 0.99851, 0.99856, 0.99861, 0.99865, 0.99869, 0.99874, 0.99878, 0.99882, 0.99886, 0.99889, 0.99893, 0.99896, 0.999, 0.99903, 0.99906, 0.9991, 0.99913, 0.99916, 0.99918, 0.99921, 0.99924, 0.99926, 0.99929, 0.99931, 0.99934, 0.99936, 0.99938, 0.9994, 0.99942, 0.99944, 0.99946, 0.99948, 0.9995, 0.99952, 0.99953, 0.99955, 0.99957, 0.99958, 0.9996, 0.99961, 0.99962, 0.99964, 0.99965, 0.99966, 0.99968, 0.99969, 0.9997, 0.99971, 0.99972, 0.99973, 0.99974, 0.99975, 0.99976, 0.99977, 0.99978, 0.99978, 0.99979, 0.9998, 0.99981, 0.99981, 0.99982, 0.99983, 0.99983, 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.9999, 0.9999, 0.9999, 0.99991, 0.99991, 0.99992, 0.99992, 0.99992, 0.99992, 0.99993, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, 0.99995, 0.99995, 0.99995, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99998 };
for(int i=0; i < 410; i++)
{
m_NormalDistValues[i] = temp[i];
}
}
} // end namespace itk
#endif
diff --git a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
index b587208387..09a04be161 100644
--- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
+++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
@@ -1,542 +1,542 @@
/*============================================================================
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 <mitkHotspotMaskGenerator.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageCast.h>
#include <mitkPoint.h>
#include <itkImageRegionIterator.h>
#include "mitkImageAccessByItk.h"
#include <itkImageDuplicator.h>
#include <itkFFTConvolutionImageFilter.h>
#include <itkVnlFFTImageFilterInitFactory.h>
#include <mitkITKImageImport.h>
namespace mitk
{
HotspotMaskGenerator::HotspotMaskGenerator():
m_HotspotRadiusInMM(6.2035049089940), // radius of a 1cm3 sphere in mm
m_HotspotMustBeCompletelyInsideImage(true),
m_Label(1)
{
m_InternalMask = mitk::Image::New();
m_InternalMaskUpdateTime = 0;
}
HotspotMaskGenerator::~HotspotMaskGenerator()
{
}
unsigned int HotspotMaskGenerator::GetNumberOfMasks() const
{
return 1;
}
mitk::Image::ConstPointer HotspotMaskGenerator::DoGetMask(unsigned int)
{
if (IsUpdateRequired())
{
if ( m_InputImage.IsNull() )
{
throw std::runtime_error( "Error: image empty!" );
}
if ( m_InputImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint) )
{
throw std::runtime_error( "Error: invalid time point!" );
}
auto timeSliceImage = SelectImageByTimePoint(m_InputImage, m_TimePoint);
m_internalMask2D = nullptr; // is this correct when this variable holds a smart pointer?
m_internalMask3D = nullptr;
if ( m_Mask != nullptr )
{
m_Mask->SetTimePoint(m_TimePoint);
mitk::Image::ConstPointer timeSliceMask = m_Mask->GetMask(0);
if ( timeSliceImage->GetDimension() == 3 )
{
- itk::Image<unsigned short, 3>::Pointer noneConstMaskImage; //needed to work arround the fact that CastToItkImage currently does not support const itk images.
+ itk::Image<unsigned short, 3>::Pointer noneConstMaskImage; //needed to work around the fact that CastToItkImage currently does not support const itk images.
CastToItkImage(timeSliceMask, noneConstMaskImage);
m_internalMask3D = noneConstMaskImage;
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 3, m_internalMask3D.GetPointer(), m_Label);
}
else if ( timeSliceImage->GetDimension() == 2 )
{
- itk::Image<unsigned short, 2>::Pointer noneConstMaskImage; //needed to work arround the fact that CastToItkImage currently does not support const itk images.
+ itk::Image<unsigned short, 2>::Pointer noneConstMaskImage; //needed to work around the fact that CastToItkImage currently does not support const itk images.
CastToItkImage(timeSliceMask, noneConstMaskImage);
m_internalMask2D = noneConstMaskImage;
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 2, m_internalMask2D.GetPointer(), m_Label);
}
else
{
throw std::runtime_error( "Error: invalid image dimension" );
}
}
else
{
if ( timeSliceImage->GetDimension() == 3 )
{
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 3, m_internalMask3D.GetPointer(), m_Label);
}
else if ( timeSliceImage->GetDimension() == 2 )
{
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 2, m_internalMask2D.GetPointer(), m_Label);
}
else
{
throw std::runtime_error( "Error: invalid image dimension" );
}
}
this->Modified();
}
m_InternalMaskUpdateTime = m_InternalMask->GetMTime();
return m_InternalMask;
}
template <typename TPixel, unsigned int VImageDimension >
HotspotMaskGenerator::ImageExtrema
HotspotMaskGenerator::CalculateExtremaWorld( const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
- double neccessaryDistanceToImageBorderInMM,
+ double necessaryDistanceToImageBorderInMM,
unsigned int label )
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> MaskImageIteratorType;
typedef itk::ImageRegionConstIteratorWithIndex<ImageType> InputImageIndexIteratorType;
typename ImageType::SpacingType spacing = inputImage->GetSpacing();
ImageExtrema minMax;
minMax.Defined = false;
minMax.MaxIndex.set_size(VImageDimension);
minMax.MaxIndex.set_size(VImageDimension);
typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion();
- bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 );
+ bool keepDistanceToImageBorders( necessaryDistanceToImageBorderInMM > 0 );
if (keepDistanceToImageBorders)
{
itk::IndexValueType distanceInPixels[VImageDimension];
for(unsigned short dimension = 0; dimension < VImageDimension; ++dimension)
{
// To confirm that the whole hotspot is inside the image we have to keep a specific distance to the image-borders, which is as long as
// the radius. To get the amount of indices we divide the radius by spacing and add 0.5 because voxels are center based:
// For example with a radius of 2.2 and a spacing of 1 two indices are enough because 2.2 / 1 + 0.5 = 2.7 => 2.
// But with a radius of 2.7 we need 3 indices because 2.7 / 1 + 0.5 = 3.2 => 3
- distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5);
+ distanceInPixels[dimension] = int( necessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5);
}
allowedExtremaRegion.ShrinkByRadius(distanceInPixels);
}
InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion);
float maxValue = itk::NumericTraits<float>::min();
float minValue = itk::NumericTraits<float>::max();
typename ImageType::IndexType maxIndex;
typename ImageType::IndexType minIndex;
for(unsigned short i = 0; i < VImageDimension; ++i)
{
maxIndex[i] = 0;
minIndex[i] = 0;
}
if (maskImage != nullptr)
{
MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
typename ImageType::IndexType imageIndex;
typename ImageType::IndexType maskIndex;
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
imageIndex = maskIndex = maskIt.GetIndex();
if(maskIt.Get() == label)
{
if( allowedExtremaRegion.IsInside(imageIndex) )
{
imageIndexIt.SetIndex( imageIndex );
double value = imageIndexIt.Get();
minMax.Defined = true;
//Calculate minimum, maximum and corresponding index-values
if( value > maxValue )
{
maxIndex = imageIndexIt.GetIndex();
maxValue = value;
}
if(value < minValue )
{
minIndex = imageIndexIt.GetIndex();
minValue = value;
}
}
}
}
}
else
{
for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt)
{
double value = imageIndexIt.Get();
minMax.Defined = true;
//Calculate minimum, maximum and corresponding index-values
if( value > maxValue )
{
maxIndex = imageIndexIt.GetIndex();
maxValue = value;
}
if(value < minValue )
{
minIndex = imageIndexIt.GetIndex();
minValue = value;
}
}
}
minMax.MaxIndex.set_size(VImageDimension);
minMax.MinIndex.set_size(VImageDimension);
for(unsigned int i = 0; i < minMax.MaxIndex.size(); ++i)
{
minMax.MaxIndex[i] = maxIndex[i];
}
for(unsigned int i = 0; i < minMax.MinIndex.size(); ++i)
{
minMax.MinIndex[i] = minIndex[i];
}
minMax.Max = maxValue;
minMax.Min = minValue;
return minMax;
}
template <unsigned int VImageDimension>
itk::Size<VImageDimension>
HotspotMaskGenerator::CalculateConvolutionKernelSize( double spacing[VImageDimension],
double radiusInMM )
{
typedef itk::Image< float, VImageDimension > KernelImageType;
typedef typename KernelImageType::SizeType SizeType;
SizeType maskSize;
for(unsigned int i = 0; i < VImageDimension; ++i)
{
maskSize[i] = static_cast<int>( 2 * radiusInMM / spacing[i]);
// We always want an uneven size to have a clear center point in the convolution mask
if(maskSize[i] % 2 == 0 )
{
++maskSize[i];
}
}
return maskSize;
}
template <unsigned int VImageDimension>
itk::SmartPointer< itk::Image<float, VImageDimension> >
HotspotMaskGenerator::GenerateHotspotSearchConvolutionKernel(double mmPerPixel[VImageDimension],
double radiusInMM )
{
std::stringstream ss;
for (unsigned int i = 0; i < VImageDimension; ++i)
{
ss << mmPerPixel[i];
if (i < VImageDimension -1)
ss << ",";
}
MITK_DEBUG << "Update convolution kernel for spacing (" << ss.str() << ") and radius " << radiusInMM << "mm";
double radiusInMMSquared = radiusInMM * radiusInMM;
typedef itk::Image< float, VImageDimension > KernelImageType;
typename KernelImageType::Pointer convolutionKernel = KernelImageType::New();
// Calculate size and allocate mask image
typedef typename KernelImageType::SizeType SizeType;
SizeType maskSize = this->CalculateConvolutionKernelSize<VImageDimension>(mmPerPixel, radiusInMM);
mitk::Point3D convolutionMaskCenterIndex;
convolutionMaskCenterIndex.Fill(0.0);
for(unsigned int i = 0; i < VImageDimension; ++i)
{
convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1);
}
typedef typename KernelImageType::IndexType IndexType;
IndexType maskIndex;
maskIndex.Fill(0);
typedef typename KernelImageType::RegionType RegionType;
RegionType maskRegion;
maskRegion.SetSize(maskSize);
maskRegion.SetIndex(maskIndex);
convolutionKernel->SetRegions(maskRegion);
convolutionKernel->SetSpacing(mmPerPixel);
convolutionKernel->Allocate();
// Fill mask image values by subsampling the image grid
typedef itk::ImageRegionIteratorWithIndex<KernelImageType> MaskIteratorType;
MaskIteratorType maskIt(convolutionKernel,maskRegion);
int numberOfSubVoxelsPerDimension = 2; // per dimension!
int numberOfSubVoxels = ::pow( static_cast<float>(numberOfSubVoxelsPerDimension), static_cast<float>(VImageDimension) );
double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension;
double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels;
mitk::Point3D subVoxelIndexPosition;
double distanceSquared = 0.0;
typedef itk::ContinuousIndex<double, VImageDimension> ContinuousIndexType;
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
ContinuousIndexType indexPoint(maskIt.GetIndex());
mitk::Point3D voxelPosition;
for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
{
voxelPosition[dimension] = indexPoint[dimension];
}
double maskValue = 0.0;
mitk::Vector3D subVoxelOffset; subVoxelOffset.Fill(0.0);
// iterate sub-voxels by iterating all possible offsets
for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0;
subVoxelOffset[0] < +0.5;
subVoxelOffset[0] += subVoxelSizeInPixels)
{
for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0;
subVoxelOffset[1] < +0.5;
subVoxelOffset[1] += subVoxelSizeInPixels)
{
for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0;
subVoxelOffset[2] < +0.5;
subVoxelOffset[2] += subVoxelSizeInPixels)
{
- subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if neccessary (add voxelPosition to initializer and end condition)
+ subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if necessary (add voxelPosition to initializer and end condition)
distanceSquared =
(subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0]
+ (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1]
+ (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2];
if (distanceSquared <= radiusInMMSquared)
{
maskValue += valueOfOneSubVoxel;
}
}
}
}
maskIt.Set( maskValue );
}
return convolutionKernel;
}
template <typename TPixel, unsigned int VImageDimension>
itk::SmartPointer<itk::Image<TPixel, VImageDimension> >
HotspotMaskGenerator::GenerateConvolutionImage( const itk::Image<TPixel, VImageDimension>* inputImage )
{
double mmPerPixel[VImageDimension];
for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
{
mmPerPixel[dimension] = inputImage->GetSpacing()[dimension];
}
// update convolution kernel
typedef itk::Image< float, VImageDimension > KernelImageType;
typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel<VImageDimension>(mmPerPixel, m_HotspotRadiusInMM);
// update convolution image
typedef itk::Image< TPixel, VImageDimension > InputImageType;
typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
typedef itk::FFTConvolutionImageFilter<InputImageType,
KernelImageType,
ConvolutionImageType> ConvolutionFilterType;
itk::VnlFFTImageFilterInitFactory::RegisterFactories();
typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New();
typedef itk::ConstantBoundaryCondition<InputImageType, InputImageType> BoundaryConditionType;
BoundaryConditionType boundaryCondition;
boundaryCondition.SetConstant(0.0);
if (m_HotspotMustBeCompletelyInsideImage)
{
// overwrite default boundary condition
convolutionFilter->SetBoundaryCondition(&boundaryCondition);
}
convolutionFilter->SetInput(inputImage);
convolutionFilter->SetKernelImage(convolutionKernel);
convolutionFilter->SetNormalize(true);
MITK_DEBUG << "Update Convolution image for hotspot search";
convolutionFilter->UpdateLargestPossibleRegion();
typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput();
convolutionImage->SetSpacing( inputImage->GetSpacing() ); // only workaround because convolution filter seems to ignore spacing of input image
return convolutionImage;
}
template < typename TPixel, unsigned int VImageDimension>
void
HotspotMaskGenerator::FillHotspotMaskPixels( itk::Image<TPixel, VImageDimension>* maskImage,
itk::Point<double, VImageDimension> sphereCenter,
double sphereRadiusInMM )
{
typedef itk::Image< TPixel, VImageDimension > MaskImageType;
typedef itk::ImageRegionIteratorWithIndex<MaskImageType> MaskImageIteratorType;
MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
typename MaskImageType::IndexType maskIndex;
typename MaskImageType::PointType worldPosition;
// this is not very smart. I would rather use a 0 initialized mask (not the case here -> blame CalculateHotspotMask) and find the region where I need to iterate over, then iterate only over the small region
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
maskIndex = maskIt.GetIndex();
maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition);
maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 );
}
}
template <typename TPixel, unsigned int VImageDimension>
void
HotspotMaskGenerator::CalculateHotspotMask(const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
unsigned int label)
{
typedef itk::Image< TPixel, VImageDimension > InputImageType;
typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage);
if (convolutionImage.IsNull())
{
MITK_ERROR << "Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error).";
throw std::logic_error("Empty convolution image in CalculateHotspotStatistics()");
}
typename MaskImageType::ConstPointer usedMask = maskImage;
// if mask image is not defined, create an image of the same size as inputImage and fill it with 1's
// there is maybe a better way to do this!?
if (maskImage == nullptr)
{
auto defaultMask = MaskImageType::New();
typename MaskImageType::RegionType maskRegion = inputImage->GetLargestPossibleRegion();
typename MaskImageType::SpacingType maskSpacing = inputImage->GetSpacing();
typename MaskImageType::PointType maskOrigin = inputImage->GetOrigin();
typename MaskImageType::DirectionType maskDirection = inputImage->GetDirection();
defaultMask->SetRegions(maskRegion);
defaultMask->Allocate();
defaultMask->SetOrigin(maskOrigin);
defaultMask->SetSpacing(maskSpacing);
defaultMask->SetDirection(maskDirection);
defaultMask->FillBuffer(1);
usedMask = defaultMask;
label = 1;
}
// find maximum in convolution image, given the current mask
double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusInMM : -1.0;
ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), usedMask.GetPointer(), requiredDistanceToBorder, label);
bool isHotspotDefined = convolutionImageInformation.Defined;
if (!isHotspotDefined)
{
MITK_ERROR << "No origin of hotspot-sphere was calculated!";
m_InternalMask = nullptr;
}
else
{
// create a binary mask around the "hotspot" region, fill the shape of a sphere around our hotspot center
// typename DuplicatorType::Pointer copyMachine = DuplicatorType::New();
// copyMachine->SetInputImage(inputImage);
// copyMachine->Update();
// typename CastFilterType::Pointer caster = CastFilterType::New();
// caster->SetInput( copyMachine->GetOutput() );
// caster->Update();
typename MaskImageType::Pointer hotspotMaskITK = MaskImageType::New();
hotspotMaskITK->SetOrigin(inputImage->GetOrigin());
hotspotMaskITK->SetSpacing(inputImage->GetSpacing());
hotspotMaskITK->SetLargestPossibleRegion(inputImage->GetLargestPossibleRegion());
hotspotMaskITK->SetBufferedRegion(inputImage->GetBufferedRegion());
hotspotMaskITK->SetDirection(inputImage->GetDirection());
hotspotMaskITK->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel());
hotspotMaskITK->Allocate();
hotspotMaskITK->FillBuffer(1);
typedef typename InputImageType::IndexType IndexType;
IndexType maskCenterIndex;
for (unsigned int d =0; d< VImageDimension;++d)
{
maskCenterIndex[d]=convolutionImageInformation.MaxIndex[d];
}
typename ConvolutionImageType::PointType maskCenter;
inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter);
FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, m_HotspotRadiusInMM);
//obtain mitk::Image::Pointer from itk::Image
mitk::Image::Pointer hotspotMaskAsMITKImage = mitk::GrabItkImageMemory(hotspotMaskITK);
m_InternalMask = hotspotMaskAsMITKImage;
m_ConvolutionImageMaxIndex = convolutionImageInformation.MaxIndex;
m_ConvolutionImageMinIndex = convolutionImageInformation.MinIndex;
}
}
bool HotspotMaskGenerator::IsUpdateRequired() const
{
unsigned long thisClassTimeStamp = this->GetMTime();
unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime();
unsigned long maskGeneratorTimeStamp = m_Mask->GetMTime();
unsigned long inputImageTimeStamp = m_InputImage->GetMTime();
if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed
{
return true;
}
if (m_InternalMaskUpdateTime < maskGeneratorTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class
{
return true;
}
if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class
{
return true;
}
return false;
}
}
diff --git a/Modules/ImageStatistics/mitkHotspotMaskGenerator.h b/Modules/ImageStatistics/mitkHotspotMaskGenerator.h
index ba40564049..996242cfc9 100644
--- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.h
+++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.h
@@ -1,155 +1,155 @@
/*============================================================================
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 mitkHotspotMaskGenerator_h
#define mitkHotspotMaskGenerator_h
#include <itkObject.h>
#include <mitkImage.h>
#include <itkImage.h>
#include <itkTimeStamp.h>
#include <stdexcept>
#include <MitkImageStatisticsExports.h>
#include <mitkImageTimeSelector.h>
#include <mitkMaskGenerator.h>
namespace mitk
{
/**
* @warning Until T30375 is not clarified the class should be deemed deprecated/erroneous and should not
* be used
* @brief The HotspotMaskGenerator class is used when a hotspot has to be found in an image. A hotspot is
* the region of the image where the mean intensity is maximal (=brightest spot). It is usually used in PET scans.
* The identification of the hotspot is done as follows: First a cubic (or circular, if image is 2d)
* mask of predefined size is generated. This mask is then convolved with the input image (in fourier domain).
* The maximum value of the convolved image then corresponds to the hotspot.
* If a maskGenerator is set, only the pixels of the convolved image where the corresponding mask is == @a label
* are searched for the maximum value.
*/
class MITKIMAGESTATISTICS_EXPORT HotspotMaskGenerator: public MaskGenerator
{
public:
/** Standard Self typedef */
typedef HotspotMaskGenerator Self;
typedef MaskGenerator Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self); /** Runtime information support. */
itkTypeMacro(HotspotMaskGenerator, MaskGenerator);
unsigned int GetNumberOfMasks() const override;
/**
@brief Set a mask (can be nullptr if no mask is desired)
*/
itkSetObjectMacro(Mask, MaskGenerator);
/**
@brief Set the radius of the hotspot (in MM)
*/
itkGetConstMacro(HotspotRadiusInMM, double);
itkSetMacro(HotspotRadiusInMM, double);
/**
@brief Define whether the hotspot must be completely inside the image. Default is true
*/
itkGetConstMacro(HotspotMustBeCompletelyInsideImage, bool);
itkSetMacro(HotspotMustBeCompletelyInsideImage, bool);
/**
- @brief If a maskGenerator is set, this detemines which mask value is used
+ @brief If a maskGenerator is set, this determines which mask value is used
*/
itkSetMacro(Label, unsigned short);
protected:
HotspotMaskGenerator();
~HotspotMaskGenerator() override;
Image::ConstPointer DoGetMask(unsigned int) override;
class ImageExtrema
{
public:
bool Defined;
double Max;
double Min;
vnl_vector<int> MaxIndex;
vnl_vector<int> MinIndex;
ImageExtrema()
:Defined(false)
,Max(itk::NumericTraits<double>::min())
,Min(itk::NumericTraits<double>::max())
{
}
};
private:
/** \brief Returns size of convolution kernel depending on spacing and radius. */
template <unsigned int VImageDimension>
itk::Size<VImageDimension>
CalculateConvolutionKernelSize(double spacing[VImageDimension], double radiusInMM);
/** \brief Generates image of kernel which is needed for convolution. */
template <unsigned int VImageDimension>
itk::SmartPointer< itk::Image<float, VImageDimension> >
GenerateHotspotSearchConvolutionKernel(double spacing[VImageDimension], double radiusInMM);
/** \brief Convolves image with spherical kernel image. Used for hotspot calculation. */
template <typename TPixel, unsigned int VImageDimension>
itk::SmartPointer< itk::Image<TPixel, VImageDimension> >
GenerateConvolutionImage( const itk::Image<TPixel, VImageDimension>* inputImage );
/** \brief Fills pixels of the spherical hotspot mask. */
template < typename TPixel, unsigned int VImageDimension>
void
FillHotspotMaskPixels( itk::Image<TPixel, VImageDimension>* maskImage,
itk::Point<double, VImageDimension> sphereCenter,
double sphereRadiusInMM);
/** \brief */
template <typename TPixel, unsigned int VImageDimension>
void
CalculateHotspotMask(const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
unsigned int label);
template <typename TPixel, unsigned int VImageDimension >
ImageExtrema CalculateExtremaWorld( const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
- double neccessaryDistanceToImageBorderInMM,
+ double necessaryDistanceToImageBorderInMM,
unsigned int label);
bool IsUpdateRequired() const;
HotspotMaskGenerator(const HotspotMaskGenerator &);
HotspotMaskGenerator & operator=(const HotspotMaskGenerator &);
MaskGenerator::Pointer m_Mask;
mitk::Image::Pointer m_InternalMask;
itk::Image<unsigned short, 2>::ConstPointer m_internalMask2D;
itk::Image<unsigned short, 3>::ConstPointer m_internalMask3D;
double m_HotspotRadiusInMM;
bool m_HotspotMustBeCompletelyInsideImage;
unsigned short m_Label;
vnl_vector<int> m_ConvolutionImageMinIndex, m_ConvolutionImageMaxIndex;
unsigned long m_InternalMaskUpdateTime;
};
}
#endif
diff --git a/Modules/ImageStatistics/mitkMaskGenerator.h b/Modules/ImageStatistics/mitkMaskGenerator.h
index 2e5cf11c1c..7725639e63 100644
--- a/Modules/ImageStatistics/mitkMaskGenerator.h
+++ b/Modules/ImageStatistics/mitkMaskGenerator.h
@@ -1,73 +1,73 @@
/*============================================================================
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 mitkMaskGenerator_h
#define mitkMaskGenerator_h
#include <MitkImageStatisticsExports.h>
#include <mitkImage.h>
#include <itkRegion.h>
#include <itkObject.h>
#include <itkSmartPointer.h>
namespace mitk
{
/**
* \class MaskGenerator
* \brief Base Class for all Mask Generators. Mask generators are classes that provide functionality for the
-* creation of binary (or unsigned short) masks that can be applied to an image. See dervied classes for more
+* creation of binary (or unsigned short) masks that can be applied to an image. See derived classes for more
* information.
*/
class MITKIMAGESTATISTICS_EXPORT MaskGenerator: public itk::Object
{
public:
mitkClassMacroItkParent(MaskGenerator, itk::Object);
virtual unsigned int GetNumberOfMasks() const = 0;
/**
* @brief GetMask returns the requested (multi) label mask.
* @param maskID Parameter indicating which mask should be returned.
* @return mitk::Image::Pointer of generated mask requested
*/
mitk::Image::ConstPointer GetMask(unsigned int maskID);
/**
* @brief GetReferenceImage per default returns the inputImage (as set by SetInputImage). If no input image is set it will return a nullptr.
*/
virtual mitk::Image::ConstPointer GetReferenceImage();
/**
* @brief SetInputImage is used to set the input image to the mask generator. Some subclasses require an input image, others don't. See the documentation of the specific Mask Generator for more information.
*/
itkSetConstObjectMacro(InputImage, mitk::Image);
itkSetMacro(TimePoint, TimePointType);
protected:
MaskGenerator();
/**
* @brief DoGetMask must be overridden by derived classes.
* @param maskID Parameter indicating which mask should be returned.
* @return mitk::Image::Pointer of generated mask requested
*/
virtual mitk::Image::ConstPointer DoGetMask(unsigned int maskID) = 0;
TimePointType m_TimePoint;
mitk::Image::ConstPointer m_InputImage;
private:
};
}
#endif
diff --git a/Modules/ImageStatistics/mitkMaskUtilities.h b/Modules/ImageStatistics/mitkMaskUtilities.h
index df08a30099..2acd180df1 100644
--- a/Modules/ImageStatistics/mitkMaskUtilities.h
+++ b/Modules/ImageStatistics/mitkMaskUtilities.h
@@ -1,88 +1,88 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkMaskUtilities_h
#define mitkMaskUtilities_h
#include <MitkImageStatisticsExports.h>
#include <mitkImage.h>
#include <mitkNodePredicateGeometry.h>
#include <itkImage.h>
namespace mitk
{
/**
* @brief Utility class for mask operations. It checks whether an image and a mask are compatible (spacing, orientation, etc...)
* and it can also crop an image to the LargestPossibleRegion of the Mask
*/
template <class TPixel, unsigned int VImageDimension>
class MaskUtilities: public itk::Object
{
public:
/** Standard Self typedef */
typedef MaskUtilities Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self); /** Runtime information support. */
itkTypeMacro(MaskUtilities, itk::Object);
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskType;
/**
* @brief Set image
*/
void SetImage(const ImageType* image);
/**
* @brief Set mask
*/
void SetMask(const MaskType* mask);
/**
* @brief Checks whether mask and image are compatible for joint access (as via iterators).
* Spacing and direction must be the same between the two and they must be aligned. Also, the mask must be completely inside the image
*/
bool CheckMaskSanity();
/**
* @brief Crops the image to the LargestPossibleRegion of the mask
*/
typename ImageType::ConstPointer ExtractMaskImageRegion();
protected:
MaskUtilities(): m_Image(nullptr), m_Mask(nullptr){}
~MaskUtilities() override{}
private:
const ImageType* m_Image;
const MaskType* m_Mask;
};
/** Tolerance used to check if the mask and input image are compatible for
- * coordinate aspects (orgin, size, grid alignment).*/
+ * coordinate aspects (origin, size, grid alignment).*/
constexpr double MASK_SUITABILITY_TOLERANCE_COORDINATE = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION;
/** Tolerance used to check if the mask and input image are compatible for
* direction aspects (orientation of mask and image).*/
constexpr double MASK_SUITABILITY_TOLERANCE_DIRECTION = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION;
}
#ifndef ITK_MANUAL_INSTANTIATION
#include <mitkMaskUtilities.tpp>
#endif
#endif
diff --git a/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h b/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h
index 9a2db4c055..0b52bb757d 100644
--- a/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h
+++ b/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h
@@ -1,118 +1,118 @@
/*============================================================================
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 mitkPointSetStatisticsCalculator_h
#define mitkPointSetStatisticsCalculator_h
#include <itkObject.h>
#include <MitkImageStatisticsExports.h>
#include <mitkPointSet.h>
namespace mitk
{
/**
* \brief Class for calculating statistics (like standard derivation, RMS, mean, etc.) for a PointSet.
*/
class MITKIMAGESTATISTICS_EXPORT PointSetStatisticsCalculator : public itk::Object
{
public:
mitkClassMacroItkParent( PointSetStatisticsCalculator, itk::Object );
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
mitkNewMacro1Param(PointSetStatisticsCalculator,mitk::PointSet::Pointer);
/** @brief Sets the point set which will be analysed. */
void SetPointSet(mitk::PointSet::Pointer pSet);
/** @return Returns the mean position of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/
mitk::Point3D GetPositionMean();
/** @return Returns the standard derivation of each component (x, y and z) of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/
mitk::Vector3D GetPositionStandardDeviation();
/** @return Returns the sample standard derivation of each component (x, y and z) of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/
mitk::Vector3D GetPositionSampleStandardDeviation();
- /** @return Returns the mean distance to the mean postion (=mean error) of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
+ /** @return Returns the mean distance to the mean position (=mean error) of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMean();
/** @return Returns the standard derivation of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorStandardDeviation();
/** @return Returns the sample standard derivation of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorSampleStandardDeviation();
/** @return Returns the RMS of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorRMS();
/** @return Returns the median of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMedian();
/** @return Returns the maximum of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMax();
/** @return Returns the minimum of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMin();
//#####################################################################################################
//this both methods are used by another class an so they are public... perhaps we want to move them
//out of this class because they have nothing to do with point sets.
/** @return returns the standard derivation of the given list (NOT of the point set).*/
double GetStabw(std::vector<double> list);
/** @return returns the sample standard derivation of the given list (NOT of the point set).*/
double GetSampleStabw(std::vector<double> list);
//#####################################################################################################
protected:
PointSetStatisticsCalculator();
explicit PointSetStatisticsCalculator(mitk::PointSet::Pointer);
~PointSetStatisticsCalculator() override;
// TODO: Remove the std::vector<mitk::Point3D> data structure and use mitk::PointSet everywhere
/** @return Returns a list with the distances to the mean of the list */
std::vector<double> GetErrorList(std::vector<mitk::Point3D> list);
/** @return Returns the mean of the point list. Returns [0;0;0] if the list is empty. */
mitk::Point3D GetMean(std::vector<mitk::Point3D> list);
/** @brief Converts a point set to a vector of Point3D. */
std::vector<mitk::Point3D> PointSetToVector(mitk::PointSet::Pointer pSet);
/** @return Returns true if all positions in the evaluated points set are equal. False if not. */
bool CheckIfAllPositionsAreEqual();
mitk::PointSet::Pointer m_PointSet;
double GetMean(std::vector<double> list);
double GetMedian(std::vector<double> list);
double GetMax(std::vector<double> list);
double GetMin(std::vector<double> list);
};
}
#endif
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h b/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h
index 4ba799839f..777bbd1bd0 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h
@@ -1,77 +1,77 @@
/*============================================================================
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 QmitkDataGenerationJobBase_h
#define QmitkDataGenerationJobBase_h
//QT
#include <QRunnable>
#include <QObject>
#include <QMetaType>
//MITK
#include <mitkBaseData.h>
#include <MitkImageStatisticsUIExports.h>
/*!
\brief QmitkDataGenerationJobBase
Base class for generation jobs used by QmitkDataGenerationBase and derived classes.
*/
class MITKIMAGESTATISTICSUI_EXPORT QmitkDataGenerationJobBase : public QObject, public QRunnable
{
Q_OBJECT
public:
/** Result map that indicates all results generated by the job.
The key is a job specific label for the results.*/
using ResultMapType = std::map<std::string, mitk::BaseData::Pointer>;
virtual ResultMapType GetResults() const = 0;
/** Calls RunComputation() and takes care of the error handling and result signalling.*/
void run() final;
/*!
- /brief Returns a flag the indicates if the job computation was successfull.*/
+ /brief Returns a flag the indicates if the job computation was successful.*/
bool GetComputationSuccessFlag() const;
std::string GetLastErrorMessage() const;
bool IsRunning() const;
signals:
void Error(QString err, const QmitkDataGenerationJobBase* job);
/*! @brief Signal is emitted when results are available.
@param results produced by the job and ready to be used.
@param job the job that produced the data
*/
void ResultsAvailable(ResultMapType results, const QmitkDataGenerationJobBase* job);
protected:
QmitkDataGenerationJobBase() = default;
virtual ~QmitkDataGenerationJobBase() = default;
/**Does the real computation. Returns true if there where results produced.*/
virtual bool RunComputation() = 0;
std::string m_LastErrorMessage;
private:
bool m_ComputationSuccessful = false;
bool m_IsRunning = false;
};
#endif
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkDataGeneratorBase.h b/Modules/ImageStatisticsUI/Qmitk/QmitkDataGeneratorBase.h
index 50fd450622..8ba703eec4 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkDataGeneratorBase.h
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkDataGeneratorBase.h
@@ -1,178 +1,180 @@
/*============================================================================
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 QmitkDataGeneratorBase_h
#define QmitkDataGeneratorBase_h
#include <mutex>
//QT
#include <QObject>
//MITK
#include <mitkDataStorage.h>
#include "QmitkDataGenerationJobBase.h"
#include <MitkImageStatisticsUIExports.h>
/*!
\brief QmitkDataGeneratorBase
BaseClass that implements the organization of (statistic) data generation for pairs of images and ROIs.
The key idea is that this class ensures that for vector of given image ROI pairs (defined by derived classes)
a result instance (e.g ImageStatisticsContainer) will be calculated, if needed (e.g. because it is missing or
not up to date anymore), and stored in the data storage passed to a generator instance. While derived classes i.a.
specify how to generate the image ROI pairs, how to detect latest results, what the next generation step is and
how to remove obsolete data from the storage, the base class takes care of the observation of the data storage
and orchestrates the whole checking and generation workflow.
In all the generation/orchestration process the data storage, passed to the generator, 1) serves as place where the final
results are stored and searched and 2) it resembles the state of the generation process with these final results and WIP
place holder nodes that indicate planed or currently processed generation steps.
*/
class MITKIMAGESTATISTICSUI_EXPORT QmitkDataGeneratorBase : public QObject
{
Q_OBJECT
public:
QmitkDataGeneratorBase(const QmitkDataGeneratorBase& other) = delete;
QmitkDataGeneratorBase& operator=(const QmitkDataGeneratorBase& other) = delete;
virtual ~QmitkDataGeneratorBase();
using JobResultMapType = QmitkDataGenerationJobBase::ResultMapType;
mitk::DataStorage::Pointer GetDataStorage() const;
/** Indicates if the generator may trigger the update automatically (true). Reasons for an update are:
- Input data has been changed or modified
- Generation relevant settings in derived classes have been changed (must be implemented in derived classes)
*/
bool GetAutoUpdate() const;
/** Indicates if there is currently work in progress, thus data generation jobs are running or pending.
It is set to true when GenerationStarted is triggered and becomes false as soon as GenerationFinished is triggered.
*/
bool IsGenerating() const;
/** Checks data validity and triggers generation of data, if needed.
The generation itself will be done with a thread pool and is orchestrated by this class. To learn if the threads are finished and
everything is uptodate, listen to the signal GenerationFinished.
@return indicates if everything is already valid (true) or if the generation of new data was triggered (false).*/
bool Generate() const;
/** Indicates if for a given image and ROI a valid final result is available.*/
virtual bool IsValidResultAvailable(const mitk::DataNode* imageNode, const mitk::DataNode* roiNode) const = 0;
public slots:
/** Sets the data storage the generator should monitor and where WIP placeholder nodes and final result nodes should be stored.*/
void SetDataStorage(mitk::DataStorage* storage);
void SetAutoUpdate(bool autoUpdate);
protected slots:
+ /** Used by QmitkDataGenerationJobBase to signal the generator that an error occurred. */
/** Used by QmitkDataGenerationJobBase to signal the generator that an error occurred. */
void OnJobError(QString error, const QmitkDataGenerationJobBase* failedJob) const;
/** Used by QmitkDataGenerationJobBase to signal and communicate the results of there computation. */
void OnFinalResultsAvailable(JobResultMapType results, const QmitkDataGenerationJobBase *job) const;
signals:
/*! @brief Signal that is emitted if a data generation job is started to generate outdated/inexistent data.
*/
void DataGenerationStarted(const mitk::DataNode* imageNode, const mitk::DataNode* roiNode, const QmitkDataGenerationJobBase* job) const;
/*! @brief Signal that is emitted if new final data is produced.
*/
void NewDataAvailable(mitk::DataStorage::SetOfObjects::ConstPointer data) const;
/*! @brief Signal that is emitted if all jobs are finished and everything is up to date.
*/
void GenerationFinished() const;
/*! @brief Signal that is emitted in case of job errors.
*/
void JobError(QString error, const QmitkDataGenerationJobBase* failedJob) const;
protected:
/*! @brief Constructor
@param storage the data storage where all produced data should be stored
@param parent
*/
QmitkDataGeneratorBase(mitk::DataStorage::Pointer storage, QObject* parent = nullptr);
QmitkDataGeneratorBase(QObject* parent = nullptr);
using InputPairVectorType = std::vector<std::pair<mitk::DataNode::ConstPointer, mitk::DataNode::ConstPointer>>;
/** This method must be implemented by derived to indicate if a changed node is relevant and therefore if an update must be triggered.*/
virtual bool ChangedNodeIsRelevant(const mitk::DataNode* changedNode) const = 0;
/** This method must be implemented by derived classes to return the pairs of images and ROIs
+ /** This method must be implemented by derived classes to return the pairs of images and ROIs
(ROI may be null if no ROI is needed) for which data are needed.*/
virtual InputPairVectorType GetAllImageROICombinations() const = 0;
/** This method should indicate all missing and outdated (interim) results in the data storage, with new placeholder nodes and WIP dummy data
added to the storage. The placeholder nodes will be replaced by the real results as soon as they are ready.
The strategy how to detect which placeholder node is need and how the dummy data should look like must be implemented by derived classes.*/
virtual void IndicateFutureResults(const mitk::DataNode* imageNode, const mitk::DataNode* roiNode) const = 0;
/*! @brief Is called to generate the next job instance that needs to be done and is associated dummy node
in order to progress the data generation workflow.
@remark The method can assume that the caller takes care of the job instance deletion.
@return std::pair of job pointer and placeholder node associated with the job. Following combinations are possible:
- Both are null: nothing to do;
- Both are set: there is something to do for a pending dummy node -> trigger computation;
- Job null and node set: a job for this node is already work in progress -> pass on till its finished.*/
virtual std::pair<QmitkDataGenerationJobBase*,mitk::DataNode::Pointer> GetNextMissingGenerationJob(const mitk::DataNode* imageNode, const mitk::DataNode* roiNode) const =0;
/** Remove all obsolete data nodes for the given image and ROI node from the data storage.
Obsolete nodes are (interim) result nodes that are not the most recent any more.*/
virtual void RemoveObsoleteDataNodes(const mitk::DataNode* imageNode, const mitk::DataNode* roiNode) const = 0;
/** Prepares result to be added to the storage in an appropriate way and returns the data node for that.*/
virtual mitk::DataNode::Pointer PrepareResultForStorage(const std::string& label, mitk::BaseData* result, const QmitkDataGenerationJobBase* job) const = 0;
/*! Creates a data node for WIP place holder results. It can be used by IndicateFutureResults().*/
static mitk::DataNode::Pointer CreateWIPDataNode(mitk::BaseData* dataDummy, const std::string& nodeName);
/** Filters a passed pair vector. The returned pair vector only contains pair of nodes that exist in the data storage.*/
InputPairVectorType FilterImageROICombinations(InputPairVectorType&& imageROICombinations) const;
/** Return a descriptive label of a passed pair. Used e.g. for some debug log messages.*/
std::string GetPairDescription(const InputPairVectorType::value_type& imageAndSeg) const;
/** Internal part of the generation strategy. Here is where the heavy lifting is done.*/
bool DoGenerate() const;
/** Methods either directly calls generation or if its already ongoing flags to restart the generation.*/
void EnsureRecheckingAndGeneration() const;
mitk::WeakPointer<mitk::DataStorage> m_Storage;
bool m_AutoUpdate = false;
mutable std::mutex m_DataMutex;
/** Time stamp for the last successful run through with the current image roi pairs.*/
mutable itk::TimeStamp m_GenerationTime;
private:
/** Indicates if we are currently in the Generation() verification and generation of pending jobs triggering loop.
Only needed for the internal logic.*/
mutable bool m_InGenerate = false;
/** Internal flag that is set if a generation was requested, while one generation loop was already ongoing.*/
mutable bool m_RestartGeneration = false;
/** Indicates if there are still jobs pending or computing (true) or if everything is valid (false).*/
mutable bool m_WIP = false;
/** Internal flag that indicates that generator is currently in the process of adding results to the storage*/
mutable bool m_AddingToStorage = false;
/**Member is called when a node is added to the storage.*/
void NodeAddedOrModified(const mitk::DataNode* node);
unsigned long m_DataStorageDeletedTag;
};
#endif
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
index 7797c328cb..9f1b214f0f 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.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 QmitkImageStatisticsTreeModel_h
#define QmitkImageStatisticsTreeModel_h
#include "QmitkAbstractDataStorageModel.h"
//MITK
#include <MitkImageStatisticsUIExports.h>
#include "mitkImageStatisticsContainer.h"
#include <mutex>
class QmitkImageStatisticsTreeItem;
/*!
\class QmitkImageStatisticsTreeModel
The class is used to represent the information of mitk::ImageStatisticsContainer in the set datastorage in the context of the QT view-model-concept.
The represented ImageStatisticContainer are specified by setting the image and mask nodes that should be regarded.
-In addition you may specified the statistic computation property HistorgramNBins and IgnoreZeroValueVoxel to select the correct
+In addition you may specified the statistic computation property HistogramNBins and IgnoreZeroValueVoxel to select the correct
statistics.
*/
class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTreeModel : public QmitkAbstractDataStorageModel
{
Q_OBJECT
public:
QmitkImageStatisticsTreeModel(QObject *parent = nullptr);
~QmitkImageStatisticsTreeModel() override;
void SetImageNodes(const std::vector<mitk::DataNode::ConstPointer>& nodes);
void SetMaskNodes(const std::vector<mitk::DataNode::ConstPointer>& nodes);
void Clear();
/*! /brief Set flag to ignore zero valued voxels */
void SetIgnoreZeroValueVoxel(bool _arg);
/*! /brief Get status of zero value voxel ignoring. */
bool GetIgnoreZeroValueVoxel() const;
/*! /brief Set bin size for histogram resolution.*/
void SetHistogramNBins(unsigned int nbins);
/*! /brief Get bin size for histogram resolution.*/
unsigned int GetHistogramNBins() const;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
signals:
void dataAvailable();
/** Is emitted whenever the model changes are finished (usually a bit later than dataAvailable()).*/
void modelChanged();
protected:
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void DataStorageChanged() override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodePredicateChanged() override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeAdded(const mitk::DataNode *node) override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeChanged(const mitk::DataNode *node) override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeRemoved(const mitk::DataNode *node) override;
private:
void UpdateByDataStorage();
using StatisticsContainerVector = std::vector<mitk::ImageStatisticsContainer::ConstPointer>;
/* builds a hierarchical tree model for the image statistics
1. Level: Image
--> 2. Level: Mask [if exist]
--> 3. Level: Label instances [if Mask has more then one label]
--> 4. Level: Timestep [if >1 exist] */
void BuildHierarchicalModel();
StatisticsContainerVector m_Statistics;
/** Relevant images set by the user.*/
std::vector<mitk::DataNode::ConstPointer> m_ImageNodes;
/** Helper that is constructed when m_ImageNodes is set. It has the same order
like m_ImageNodes, but each image is represented n times, while n is the number
of time steps the respective image has. This structure makes the business logic
to select the correct image given a QIndex much simpler and therefore easy to
understand/maintain. */
std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int> > m_TimeStepResolvedImageNodes;
/** relevant masks set by the user.*/
std::vector<mitk::DataNode::ConstPointer> m_MaskNodes;
/** @sa m_TimeStepResolvedImageNodes */
std::vector<std::pair<mitk::DataNode::ConstPointer, unsigned int>> m_TimeStepResolvedMaskNodes;
std::vector<std::string> m_StatisticNames;
std::mutex m_Mutex;
std::unique_ptr<QmitkImageStatisticsTreeItem> m_RootItem;
QVariant m_HeaderFirstColumn;
itk::TimeStamp m_BuildTime;
bool m_IgnoreZeroValueVoxel = false;
unsigned int m_HistogramNBins = 100;
};
#endif
diff --git a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
index 4562aed61a..d1875debb6 100644
--- a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
+++ b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
@@ -1,537 +1,537 @@
/*============================================================================
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 "QmitkImageStatisticsDataGenerator.h"
#include <QApplication>
#include <mitkStandaloneDataStorage.h>
#include "mitkImage.h"
#include "mitkPlanarFigure.h"
#include "mitkIOUtil.h"
#include "mitkStatisticsToImageRelationRule.h"
#include "mitkStatisticsToMaskRelationRule.h"
#include "mitkImageStatisticsContainerManager.h"
#include "mitkProperties.h"
#include "QmitkImageStatisticsCalculationRunnable.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
class TestQmitkImageStatisticsDataGenerator : public QmitkImageStatisticsDataGenerator
{
public:
TestQmitkImageStatisticsDataGenerator(mitk::DataStorage::Pointer storage, QObject* parent = nullptr) : QmitkImageStatisticsDataGenerator(storage, parent)
{
connect(this, &QmitkDataGeneratorBase::NewDataAvailable, this, &TestQmitkImageStatisticsDataGenerator::NewDataAvailableEmited);
connect(this, &QmitkDataGeneratorBase::DataGenerationStarted, this, &TestQmitkImageStatisticsDataGenerator::DataGenerationStartedEmited);
connect(this, &QmitkDataGeneratorBase::GenerationFinished, this, &TestQmitkImageStatisticsDataGenerator::GenerationFinishedEmited);
connect(this, &QmitkDataGeneratorBase::JobError, this, &TestQmitkImageStatisticsDataGenerator::JobErrorEmited);
};
mutable std::vector<mitk::DataStorage::SetOfObjects::ConstPointer> m_NewDataAvailable;
void NewDataAvailableEmited(mitk::DataStorage::SetOfObjects::ConstPointer data) const
{
m_NewDataAvailable.emplace_back(data);
};
mutable int m_DataGenerationStartedEmited = 0;
void DataGenerationStartedEmited(const mitk::DataNode* /*imageNode*/, const mitk::DataNode* /*roiNode*/, const QmitkDataGenerationJobBase* /*job*/) const
{
m_DataGenerationStartedEmited++;
}
mutable int m_GenerationFinishedEmited = 0;
void GenerationFinishedEmited() const
{
m_GenerationFinishedEmited++;
QCoreApplication::instance()->quit();
}
mutable std::vector<QString> m_JobErrorEmited_error;
void JobErrorEmited(QString error, const QmitkDataGenerationJobBase* /*failedJob*/) const
{
m_JobErrorEmited_error.emplace_back(error);
}
};
class QmitkImageStatisticsDataGeneratorTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(QmitkImageStatisticsDataGeneratorTestSuite);
MITK_TEST(GetterSetterTest);
MITK_TEST(NullTest);
MITK_TEST(OneImageTest);
MITK_TEST(MultiImageTest);
MITK_TEST(ImageAndROITest);
MITK_TEST(ImageAndMultiROITest);
MITK_TEST(MultiMultiTest);
MITK_TEST(InputChangedTest);
MITK_TEST(SettingsChangedTest);
MITK_TEST(DataStorageModificationTest);
CPPUNIT_TEST_SUITE_END();
mitk::DataStorage::Pointer m_DataStorage;
mitk::DataNode::Pointer m_ImageNode1;
mitk::DataNode::Pointer m_ImageNode2;
mitk::DataNode::Pointer m_MaskImageNode;
mitk::DataNode::Pointer m_PFNode;
mitk::Image::Pointer m_Image1;
mitk::Image::Pointer m_Image2;
mitk::Image::Pointer m_Mask;
mitk::PlanarFigure::Pointer m_PF;
QCoreApplication* m_TestApp;
public:
void setUp() override
{
m_DataStorage = mitk::StandaloneDataStorage::New();
m_ImageNode1 = mitk::DataNode::New();
m_ImageNode1->SetName("Image_1");
auto pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd");
m_Image1 = mitk::IOUtil::Load<mitk::Image>(pic3DCroppedFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Image1.IsNotNull());
m_ImageNode1->SetData(m_Image1);
m_DataStorage->Add(m_ImageNode1);
m_ImageNode2 = mitk::DataNode::New();
m_ImageNode2->SetName("Image_2");
m_Image2 = mitk::IOUtil::Load<mitk::Image>(pic3DCroppedFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Image2.IsNotNull());
m_ImageNode2->SetData(m_Image2);
m_DataStorage->Add(m_ImageNode2);
m_MaskImageNode = mitk::DataNode::New();
m_MaskImageNode->SetName("Mask");
auto pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd");
m_Mask = mitk::IOUtil::Load<mitk::Image>(pic3DCroppedBinMaskFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Mask.IsNotNull());
m_MaskImageNode->SetData(m_Mask);
m_DataStorage->Add(m_MaskImageNode);
m_PFNode = mitk::DataNode::New();
m_PFNode->SetName("PF");
auto pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf");
m_PF = mitk::IOUtil::Load<mitk::PlanarFigure>(pic3DCroppedPlanarFigureFile);
CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_PF.IsNotNull());
m_PFNode->SetData(m_PF);
m_DataStorage->Add(m_PFNode);
int argc = 0;
char** argv = nullptr;
m_TestApp = new QCoreApplication(argc, argv);
}
void tearDown() override
{
delete m_TestApp;
}
bool CheckResultNode(const std::vector<mitk::DataNode::Pointer> resultNodes, const mitk::DataNode* imageNode, const mitk::DataNode* roiNode, unsigned int histBin = 100, bool noZero = false)
{
for (auto& resultNode : resultNodes)
{
bool result = false;
if (resultNode && resultNode->GetData() && imageNode && imageNode->GetData())
{
auto imageRule = mitk::StatisticsToImageRelationRule::New();
result = !imageRule->GetRelationUIDs(resultNode, imageNode).empty();
if (roiNode)
{
auto maskRule = mitk::StatisticsToMaskRelationRule::New();
result = result && !maskRule->GetRelationUIDs(resultNode, roiNode).empty();
}
auto prop = resultNode->GetData()->GetProperty(mitk::STATS_HISTOGRAM_BIN_PROPERTY_NAME.c_str());
auto binProp = dynamic_cast<const mitk::UIntProperty*>(prop.GetPointer());
result = result && binProp->GetValue() == histBin;
prop = resultNode->GetData()->GetProperty(mitk::STATS_IGNORE_ZERO_VOXEL_PROPERTY_NAME.c_str());
auto zeroProp = dynamic_cast<const mitk::BoolProperty*>(prop.GetPointer());
result = result && zeroProp->GetValue() == noZero;
}
if (result)
{ //node was in the result set
return true;
}
}
return false;
}
void NullTest()
{
TestQmitkImageStatisticsDataGenerator generator(nullptr);
generator.Generate();
CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(generator.m_NewDataAvailable.empty());
generator.SetDataStorage(m_DataStorage);
generator.Generate();
CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(generator.m_NewDataAvailable.empty());
}
void GetterSetterTest()
{
TestQmitkImageStatisticsDataGenerator generator(nullptr);
CPPUNIT_ASSERT(nullptr == generator.GetDataStorage());
generator.SetDataStorage(m_DataStorage);
CPPUNIT_ASSERT(m_DataStorage == generator.GetDataStorage());
TestQmitkImageStatisticsDataGenerator generator2(m_DataStorage);
CPPUNIT_ASSERT(m_DataStorage == generator.GetDataStorage());
CPPUNIT_ASSERT_EQUAL(100u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(false, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate());
generator.SetHistogramNBins(3);
CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(false, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate());
generator.SetIgnoreZeroValueVoxel(true);
CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(true, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate());
generator.SetAutoUpdate(true);
CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins());
CPPUNIT_ASSERT_EQUAL(true, generator.GetIgnoreZeroValueVoxel());
CPPUNIT_ASSERT_EQUAL(true, generator.GetAutoUpdate());
}
void OneImageTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1 };
generator.SetImageNodes(imageNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable.front()->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void MultiImageTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1, m_ImageNode2 };
generator.SetImageNodes(imageNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2u == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front() }, m_ImageNode2, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void ImageAndROITest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1 };
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes { m_MaskImageNode };
generator.SetImageNodes(imageNodes);
generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front() }, m_ImageNode1, m_MaskImageNode));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
roiNodes = { m_PFNode };
generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, m_PFNode));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void ImageAndMultiROITest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode, m_MaskImageNode, nullptr };
generator.SetImageNodes(imageNodes);
generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(3 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front()}, m_ImageNode1, m_PFNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, m_MaskImageNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void MultiMultiTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1, m_ImageNode2 };
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode, m_MaskImageNode, nullptr };
generator.SetImageNodes(imageNodes);
generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(6, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(6 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, m_PFNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, m_MaskImageNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, nullptr));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, m_PFNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, m_MaskImageNode));
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(),
generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(),
generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, nullptr));
CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate());
}
void InputChangedTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode2 };
generator.SetImageNodes(imageNodes);
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(0 == generator.m_NewDataAvailable.size());
imageNodes = { m_ImageNode1 };
generator.SetAutoUpdate(true);
generator.SetImageNodes(imageNodes);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 1 == generator.m_NewDataAvailable.size());
generator.SetImageNodes(imageNodes);
m_TestApp->processEvents();
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1, generator.m_DataGenerationStartedEmited);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1, generator.m_GenerationFinishedEmited);
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty());
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1 == generator.m_NewDataAvailable.size());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 1, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 1, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", 1 == generator.m_NewDataAvailable.size());
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_MaskImageNode };
generator.SetAutoUpdate(true);
generator.SetROINodes(roiNodes);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 2 == generator.m_NewDataAvailable.size());
}
void SettingsChangedTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
generator.SetImageNodes(imageNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front() }, m_ImageNode1, nullptr));
generator.SetHistogramNBins(50);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, nullptr, 50));
generator.SetIgnoreZeroValueVoxel(true);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(3, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(3 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, nullptr, 50, true));
//now check auto update feature
generator.SetAutoUpdate(true);
generator.SetHistogramNBins(5);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 4, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 4, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 4 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[3]->front() }, m_ImageNode1, nullptr, 5, true));
generator.SetHistogramNBins(5);
m_TestApp->processEvents();
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4, generator.m_DataGenerationStartedEmited);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4, generator.m_GenerationFinishedEmited);
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty());
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4 == generator.m_NewDataAvailable.size());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 4, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 4, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", 4 == generator.m_NewDataAvailable.size());
generator.SetIgnoreZeroValueVoxel(false);
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 5, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 5, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 5 == generator.m_NewDataAvailable.size());
CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[4]->front() }, m_ImageNode1, nullptr, 5, false));
generator.SetIgnoreZeroValueVoxel(false);
m_TestApp->processEvents();
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5, generator.m_DataGenerationStartedEmited);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5, generator.m_GenerationFinishedEmited);
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty());
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5 == generator.m_NewDataAvailable.size());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 5, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 5, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", 5 == generator.m_NewDataAvailable.size());
}
void DataStorageModificationTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode };
generator.SetImageNodes(imageNodes);
generator.SetROINodes(roiNodes);
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
m_PF->Modified();
m_PFNode->Modified();
m_TestApp->processEvents();
CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size());
generator.Generate();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size());
//now check auto update feature
generator.SetAutoUpdate(true);
m_PF->Modified();
m_PFNode->Modified();
m_TestApp->exec();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 3, generator.m_DataGenerationStartedEmited);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 3, generator.m_GenerationFinishedEmited);
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty());
CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 3 == generator.m_NewDataAvailable.size());
m_DataStorage->Add(mitk::DataNode::New());
m_TestApp->processEvents();
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
m_Image2->Modified();
m_ImageNode2->Modified();
m_TestApp->processEvents();
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
- CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
}
};
MITK_TEST_SUITE_REGISTRATION(QmitkImageStatisticsDataGenerator)
diff --git a/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox b/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox
index 26d7678b89..5b4ce38903 100644
--- a/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox
+++ b/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox
@@ -1,26 +1,26 @@
/**
\page LegacyGLModule OpenGL Legacy Module
\tableofcontents
\section LegacyGLModuleOverview Reasons for this module
The new legacy GL module provides support of pure OpenGL rendering for Mitk. It contains all mitkGL.h related classes from the MitkCore. Modules, which used this classes in the past, now have a dependency to LegacyGL. This module is deprecated and should only be used for a short period until all mappers and props are migrated to the VTK-based rendering pipeline.
Before the integration of this module, there was a lot of code in the Mitk rendering pipeline to enable mitkGLMapper and mitkVtkMapper in parallel. In fact, both mappers render with OpenGL but vtkMapper are compatible to each other. It was not clear for developers how to implement mappers, as there were many negative examples in the form of mitkGLMapper.
-Removing direct rendering of OpenGL makes a lot of code obsolete (e.g. Enable/DisableOpenGL() in VtkPropRenderer) and prevents side effects such as that the level window was sometimes applied to the crosshair etc. Furthermore, the software architecture and design becomes clear. There is now just one way to implement a Mapper in Mitk - the mitkVtkMapper. If you are a developper and want to write GL code, you can simply write a vtkMapper in VTK code and use it inside the mitkVtkMapper.
+Removing direct rendering of OpenGL makes a lot of code obsolete (e.g. Enable/DisableOpenGL() in VtkPropRenderer) and prevents side effects such as that the level window was sometimes applied to the crosshair etc. Furthermore, the software architecture and design becomes clear. There is now just one way to implement a Mapper in Mitk - the mitkVtkMapper. If you are a developer and want to write GL code, you can simply write a vtkMapper in VTK code and use it inside the mitkVtkMapper.
\section PortExamples Examples how to port classes from GL to VTK-based mappers
The following core classes were ported from GL to VTK and may give orientation how to port an existing mapper or prop to the VTK-based rendering pipeline:
\li mitkSurfaceGLMapper2D -> mitkSurfaceVtkMapper2D
\li mitkPointSetGLMapper2D -> mitkPointSetVtkMapper2D
\li mitkPlaneGeometryGLMapper -> mitkPlaneGeometryMapper
\li vtkMitkRectangleProp -> vtkMitkRectangleProp
\section mitkVtkGLMapperWrapperDocu The mitkVtkGLMapperWrappper
LegacyGL also provides a new base class, the mitkVtkGLMapperWrapper, which can be used to wrap existing GLMappers and pretend they are common vtkProps. Examples can be found in the Modules ContourModel and PlanarFigure.
*/
\ No newline at end of file
diff --git a/Modules/LegacyIO/mitkFileSeriesReader.cpp b/Modules/LegacyIO/mitkFileSeriesReader.cpp
index a9e8a5b719..a2a4624ca5 100644
--- a/Modules/LegacyIO/mitkFileSeriesReader.cpp
+++ b/Modules/LegacyIO/mitkFileSeriesReader.cpp
@@ -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.
============================================================================*/
#include "mitkFileSeriesReader.h"
#include <itkImageFileReader.h>
#include <itksys/Directory.hxx>
#include <itksys/SystemTools.hxx>
#include <map>
bool mitk::FileSeriesReader::GenerateFileList()
{
typedef std::vector<std::string> StringContainer;
typedef std::map<unsigned int, std::string> SortedStringContainer;
if (m_FileName == "")
{
throw itk::ImageFileReaderException(__FILE__, __LINE__, "FileName must be non-empty");
}
// MITK_INFO << "FileName: "<< m_FileName <<", FilePrefix: "<< m_FilePrefix << ", FilePattern: "<< m_FilePattern <<
// std::endl;
// determine begin and end idexes of the last digit sequence in the
// filename from the sample file name
// Therefore, walk backwards from the end of the filename until
// a number is found. The string in front of the number is the prefix,
// the string after the number is the extension.
std::string basename, path;
path = itksys::SystemTools::GetFilenamePath(m_FileName);
basename = itksys::SystemTools::GetFilenameName(m_FileName);
unsigned int digitBegin = 0;
unsigned int digitEnd = 0;
bool digitFound = false;
for (unsigned int i = basename.length() - 1;; --i)
{
char character = basename[i];
if (character >= '0' && character <= '9')
{
if (!digitFound)
{
digitEnd = i;
digitBegin = i;
digitFound = true;
}
else
digitBegin = i;
}
else
{
// end of digit series found, jump out of loop!
if (digitFound)
break;
}
if (i == 0)
break;
}
//
// if there is no digit in the filename, then we have a problem
// no matching filenames can be identified!
//
if (!digitFound)
{
itkWarningMacro("Filename contains no digit!");
return false;
}
//
// determine prefix and extension start and length
//
unsigned int prefixBegin = 0;
unsigned int prefixLength = digitBegin;
unsigned int extensionBegin = digitEnd + 1;
unsigned int extensionLength = (digitEnd == basename.length() - 1 ? 0 : basename.length() - 1 - digitEnd);
unsigned int numberLength = digitEnd - digitBegin + 1;
//
// extract prefix and extension
//
std::string prefix = "";
if (prefixLength != 0)
prefix = basename.substr(prefixBegin, prefixLength);
std::string extension = "";
if (extensionLength != 0)
extension = basename.substr(extensionBegin, extensionLength);
//
// print debug information
//
/*
MITK_INFO << "digitBegin : " << digitBegin << std::endl;
MITK_INFO << "digitEnd : " << digitEnd << std::endl;
MITK_INFO << "number of digits: " << numberLength << std::endl;
MITK_INFO << "prefixBegin : " << prefixBegin << std::endl;
MITK_INFO << "prefixLength : " << prefixLength << std::endl;
MITK_INFO << "prefix : " << prefix << std::endl;
MITK_INFO << "extensionBegin : " << extensionBegin << std::endl;
MITK_INFO << "extensionLength : " << extensionLength << std::endl;
MITK_INFO << "extension : " << extension << std::endl;
*/
if ((prefixLength + extensionLength + numberLength) != basename.length())
{
throw itk::ImageFileReaderException(
__FILE__, __LINE__, "prefixLength + extensionLength + numberLength != basenameLength");
}
//
// Load Directory
//
std::string directory = itksys::SystemTools::GetFilenamePath(m_FileName);
itksys::Directory itkDir;
if (!itkDir.Load(directory.c_str()))
{
itkWarningMacro(<< "Directory " << directory << " cannot be read!");
return false;
}
//
// Get a list of all files in the directory
//
StringContainer unmatchedFiles;
// unsigned long i;
for (unsigned long i = 0; i < itkDir.GetNumberOfFiles(); i++)
{
// Only read files
std::string filename = directory + "/" + itkDir.GetFile(i);
if (itksys::SystemTools::FileIsDirectory(filename.c_str()))
continue;
// store the filenames without path
unmatchedFiles.push_back(itkDir.GetFile(i));
}
//
// Match the file list against the file prefix and extension,
// the result should be only the files that should be read
//
StringContainer matchedFiles;
for (auto it = unmatchedFiles.begin(); it != unmatchedFiles.end(); ++it)
{
bool prefixMatch = false;
bool extensionMatch = false;
// check if the file prefix matches the current file
if (prefixLength != 0)
prefixMatch = (it->find(prefix) == prefixBegin); // check if prefix is found
else
prefixMatch = (((*it)[0] >= '0') && ((*it)[0] <= '9')); // check if filename begins with digit
// check if the file extension matches the current file
if (extensionLength != 0)
extensionMatch = (it->find(extension) == it->length() - extensionLength); // check if prefix is found
else
extensionMatch =
(((*it)[it->length() - 1] >= '0') && ((*it)[it->length() - 1] <= '9')); // check if filename ends with digit
if (prefixMatch && extensionMatch)
{
matchedFiles.push_back(*it);
}
}
if (matchedFiles.size() == 0)
{
itkWarningMacro(<< "Sorry, none of the files matched the prefix!");
return false;
}
//
// parse the file names from back to front for digits
// and convert them to a number. Store the filename and number
// in a SortedStringContainer
//
SortedStringContainer sortedFiles;
for (auto it = matchedFiles.begin(); it != matchedFiles.end(); ++it)
{
// parse the filename starting from pos digitBegin until we reach a non-digit
// or the end of filename
std::string number = "";
std::string currentFilename(*it);
for (unsigned int i = digitBegin; i < currentFilename.length(); ++i)
{
char character = currentFilename[i];
// do we have a digit?
if (character >= '0' && character <= '9')
number += character;
else
break; // end of digit series found, jump out of loop!
}
if (number.length() == 0)
{
// The file is not numbered, this is an error!
// Nevertheless, we try the next files.
itkWarningMacro(<< "The filename " << *it
<< "does not contain a valid digit sequence but matches prefix and extension. Skipping file!");
}
else
{
if ((number.length() + prefix.length() + extension.length()) != it->length())
{
itkWarningMacro(
"The file "
<< *it
- << " matches prefix and extension, but the string in beteen is not a single digit-sequence. Skipping file!");
+ << " matches prefix and extension, but the string in between is not a single digit-sequence. Skipping file!");
}
else
{
// convert the number string into an integer and
- // insert the filname (including directory) into the SortedStringContainer
+ // insert the filename (including directory) into the SortedStringContainer
unsigned int num = atoi(number.c_str());
sortedFiles.insert(std::make_pair(num, directory + "/" + *it));
}
}
}
if (sortedFiles.size() == 0)
{
itkWarningMacro(<< "Sorry, no numbered files found, I can't load anything...");
return false;
}
//
// Convert the sorted string container in a plain sorted vector of strings;
//
m_MatchedFileNames.clear();
m_MatchedFileNames.resize(sortedFiles.size());
unsigned long index = 0;
for (auto it = sortedFiles.begin(); it != sortedFiles.end(); ++it, ++index)
{
m_MatchedFileNames[index] = it->second;
MITK_INFO << "Added " << it->second << " to the set of matched files!" << std::endl;
}
return true;
}
mitk::FileSeriesReader::MatchedFileNames mitk::FileSeriesReader::GetMatchedFileNames()
{
return m_MatchedFileNames;
}
mitk::FileSeriesReader::FileSeriesReader() : m_FileName(""), m_FilePrefix(""), m_FilePattern("")
{
}
mitk::FileSeriesReader::~FileSeriesReader()
{
}
diff --git a/Modules/LegacyIO/mitkItkPictureWrite.cpp b/Modules/LegacyIO/mitkItkPictureWrite.cpp
index 96f77c9ca2..bab59f0f0c 100644
--- a/Modules/LegacyIO/mitkItkPictureWrite.cpp
+++ b/Modules/LegacyIO/mitkItkPictureWrite.cpp
@@ -1,177 +1,177 @@
/*============================================================================
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 <MitkLegacyIOExports.h>
#include "mitkItkPictureWrite.h"
#include <mitkInstantiateAccessFunctions.h>
#include <itkImageSeriesWriter.h>
#include <itkNumericSeriesFileNames.h>
#include <itkRescaleIntensityImageFilter.h>
#include <itkRGBAPixel.h>
/** Set the filenames to the specified writer in dependace on the number of images passed in */
template <class WriterType>
void SetOutputNames(typename WriterType::Pointer writer, const std::string &baseFileName, unsigned int numberOfImages)
{
if (numberOfImages > 1)
{
itk::NumericSeriesFileNames::Pointer numericFileNameWriter = itk::NumericSeriesFileNames::New();
std::string finalFileName = baseFileName;
std::string::size_type pos = baseFileName.find_last_of(".", baseFileName.length() - 1);
if (pos == std::string::npos)
finalFileName.append(".%d.png");
else
finalFileName.insert(pos, ".%d");
MITK_DEBUG << "Filename: " << finalFileName;
numericFileNameWriter->SetEndIndex(numberOfImages);
numericFileNameWriter->SetSeriesFormat(finalFileName.c_str());
numericFileNameWriter->Modified();
writer->SetFileNames(numericFileNameWriter->GetFileNames());
}
// if the given image is an 2D-png image, do not use the numericFileNameWriter
// to generate the name, since it alters the fileName given as parameter
else
{
writer->SetFileName(baseFileName.c_str());
}
}
template <typename TPixel, unsigned int VImageDimension>
void _mitkItkPictureWrite(itk::Image<TPixel, VImageDimension> *itkImage, const std::string &fileName)
{
typedef itk::Image<TPixel, VImageDimension> TImageType;
typedef itk::Image<unsigned char, 3> UCharOutputImage3DType;
typedef itk::Image<unsigned short, 3> ShortOutputImage3DType;
typedef itk::Image<unsigned char, 2> OutputImage2D_8bitType;
typedef itk::Image<unsigned short, 2> OutputImage2D_16bitType;
typedef itk::ImageSeriesWriter<UCharOutputImage3DType, OutputImage2D_8bitType> UCharWriterType;
typedef itk::ImageSeriesWriter<ShortOutputImage3DType, OutputImage2D_16bitType> ShortWriterType;
typedef itk::RescaleIntensityImageFilter<TImageType, UCharOutputImage3DType> UCharRescalerFilterType;
typedef itk::RescaleIntensityImageFilter<TImageType, ShortOutputImage3DType> ShortRescalerFilterType;
// get the size info
size_t inputTypeSize = sizeof(TPixel);
size_t supportedOutputMaxSize = 1; // default value 8bit
// the PNG and TIFF formats can handle up-to 16-bit images
if (fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos)
{
supportedOutputMaxSize = 2;
}
// get the dimension info
unsigned int numberOfImages = 1;
if (itkImage->GetImageDimension() > 2)
numberOfImages = itkImage->GetLargestPossibleRegion().GetSize()[2];
typename ShortRescalerFilterType::Pointer sh_rescaler = ShortRescalerFilterType::New();
sh_rescaler->SetInput(itkImage);
sh_rescaler->SetOutputMinimum(0);
sh_rescaler->SetOutputMaximum(65535);
typename UCharRescalerFilterType::Pointer rescaler = UCharRescalerFilterType::New();
rescaler->SetInput(itkImage);
rescaler->SetOutputMinimum(0);
rescaler->SetOutputMaximum(255);
try
{
// input is 8 bit
if (inputTypeSize == 1)
{
UCharWriterType::Pointer writer = UCharWriterType::New();
SetOutputNames<UCharWriterType>(writer, fileName, numberOfImages);
writer->SetInput(rescaler->GetOutput());
writer->Update();
}
// input pixel type is 16bit -> writer can handle 16bit images
else if (inputTypeSize == supportedOutputMaxSize && supportedOutputMaxSize == 2)
{
ShortWriterType::Pointer writer = ShortWriterType::New();
SetOutputNames<ShortWriterType>(writer, fileName, numberOfImages);
writer->SetInput(sh_rescaler->GetOutput());
writer->Update();
}
// rescaling input to maximum of supported format
else
{
if (supportedOutputMaxSize == 2)
{
typename ShortWriterType::Pointer writer = ShortWriterType::New();
SetOutputNames<ShortWriterType>(writer, fileName, numberOfImages);
writer->SetInput(sh_rescaler->GetOutput());
writer->Update();
}
else
{
typename UCharWriterType::Pointer writer = UCharWriterType::New();
SetOutputNames<UCharWriterType>(writer, fileName, numberOfImages);
writer->SetInput(rescaler->GetOutput());
writer->Update();
}
}
}
catch (const itk::ExceptionObject &e)
{
- MITK_ERROR << "ITK Exception occured: " << e.what();
+ MITK_ERROR << "ITK Exception occurred: " << e.what();
mitkThrow() << "Caught ITK exception while writing image with scalar type \n" << e.what();
}
}
template <typename TPixel, unsigned int VImageDimension>
void _mitkItkPictureWriteComposite(itk::Image<TPixel, VImageDimension> *itkImage, const std::string &fileName)
{
typedef itk::Image<TPixel, VImageDimension> TImageType;
typedef itk::Image<TPixel, 2> TImageType2D;
typedef itk::ImageSeriesWriter<TImageType, TImageType2D> WriterType;
typename WriterType::Pointer writer = WriterType::New();
// get the dimension info
unsigned int numberOfImages = 1;
if (itkImage->GetImageDimension() > 2)
numberOfImages = itkImage->GetLargestPossibleRegion().GetSize()[2];
// create output name(s)
SetOutputNames<WriterType>(writer, fileName, numberOfImages);
writer->SetInput(itkImage);
try
{
writer->Update();
}
catch (const itk::ExceptionObject &e)
{
- MITK_ERROR << "ITK Exception occured: " << e.what();
+ MITK_ERROR << "ITK Exception occurred: " << e.what();
mitkThrow() << "Caught ITK exception while writing image with composite type \n" << e.what();
}
}
#define InstantiateAccessFunction__mitkItkPictureWrite(pixelType, dim) \
template MITKLEGACYIO_EXPORT void _mitkItkPictureWrite(itk::Image<pixelType, dim> *, const std::string &);
#define InstantiateAccessFunction__mitkItkPictureWriteComposite(pixelType, dim) \
template MITKLEGACYIO_EXPORT void _mitkItkPictureWriteComposite(itk::Image<pixelType, dim> *, const std::string &);
InstantiateAccessFunction(_mitkItkPictureWrite)
InstantiateAccessFunctionForFixedPixelType(
_mitkItkPictureWriteComposite, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ)
diff --git a/Modules/LegacyIO/mitkPointSetReader.h b/Modules/LegacyIO/mitkPointSetReader.h
index 2ecb17ed7c..e0e139f774 100644
--- a/Modules/LegacyIO/mitkPointSetReader.h
+++ b/Modules/LegacyIO/mitkPointSetReader.h
@@ -1,145 +1,145 @@
/*============================================================================
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 mitkPointSetReader_h
#define mitkPointSetReader_h
#include <MitkLegacyIOExports.h>
#include <mitkFileReader.h>
#include <mitkPointSetSource.h>
#include <stack>
#include <string>
#include <vtkXMLParser.h>
namespace tinyxml2
{
class XMLElement;
}
namespace mitk
{
/**
* @brief reads xml representations of mitk::PointSets from a file
*
- * Reader for xml files containing one or multiple xml represenations of
+ * Reader for xml files containing one or multiple xml representations of
* mitk::PointSets. If multiple mitk::PointSets are stored in one file,
* these are assigned to multiple outputs of the filter. The number of point
* sets which have be read can be retrieven by a call to GetNumberOfOutputs()
* after the pipeline update().
* The reader is able to read the old 3D Pointsets without the "specification" and "timeseries" tags and the new 4D
* Pointsets.
* @note loading point sets from multiple files according to a given file pattern
* is not yet supported!
*
* @ingroup MitkLegacyIOModule
*
* @deprecatedSince{2014_10} Use mitk::IOUtils or mitk::FileReaderRegistry instead.
*/
class MITKLEGACYIO_EXPORT PointSetReader : public PointSetSource, public FileReader
{
public:
mitkClassMacro(PointSetReader, FileReader);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* @brief Sets the filename of the file to be read
* @param _arg the filename of the point set xml-file
*/
itkSetStringMacro(FileName);
/**
* @brief Returns the filename of the point set xml-file.
* @returns the filename of the point set xml-file.
*/
itkGetStringMacro(FileName);
/**
* @warning multiple load not (yet) supported
*/
itkSetStringMacro(FilePrefix);
/**
* @warning multiple load not (yet) supported
*/
itkGetStringMacro(FilePrefix);
/**
* @warning multiple load not (yet) supported
*/
itkSetStringMacro(FilePattern);
/**
* @warning multiple load not (yet) supported
*/
itkGetStringMacro(FilePattern);
static bool CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern);
/**
* @returns whether the last read attempt was successful or not.
*/
bool GetSuccess() const;
protected:
/**
* Constructor
*/
PointSetReader();
/**
* Virtual destructor
*/
~PointSetReader() override;
/**
* Actually reads the point sets from the given file
*/
void GenerateData() override;
virtual mitk::PointSet::Pointer ReadPoint(mitk::PointSet::Pointer newPointSet,
const tinyxml2::XMLElement *currentTimeSeries,
unsigned int currentTimeStep);
/**
* Does nothing in the current implementation
*/
void GenerateOutputInformation() override;
/**
* Resizes the output-objects according to the given number.
* @param num the new number of output objects.
*/
virtual void ResizeOutputs(const unsigned int &num);
/**
* Checks if the given file has appropriate
* read access.
* @returns true if the file exists and may be read
* or false otherwise.
*/
virtual int CanReadFile(const char *name);
std::string m_FileName;
std::string m_FilePrefix;
std::string m_FilePattern;
bool m_Success;
};
}
#endif
diff --git a/Modules/LegacyIO/vtkPointSetXMLParser.h b/Modules/LegacyIO/vtkPointSetXMLParser.h
index e18b85e9b5..089049a2ca 100644
--- a/Modules/LegacyIO/vtkPointSetXMLParser.h
+++ b/Modules/LegacyIO/vtkPointSetXMLParser.h
@@ -1,132 +1,132 @@
/*============================================================================
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 _VTK_POINT_SET_XML_READER__H_
#define _VTK_POINT_SET_XML_READER__H_
#include <MitkLegacyIOExports.h>
#include <list>
#include <mitkPointSet.h>
#include <stack>
#include <string>
#include <vtkXMLParser.h>
namespace mitk
{
/**
* @brief Implementation of the vtkXMLParser interface for reading mitk::PointSets.
*
* This class implements the XMLParser interface of the vtkXMLParser which is based
* on expat. It is used by the mitk::PointSetReader and is NOT INTENDED TO BE USED
* FROM THE END-USER. If you want to read point sets, use the mitk::PointSetReader.
* @ingroup MitkLegacyIOModule
*
* @deprecatedSince{2014_10} Use mitk::IOUtils or mitk::FileReaderRegistry instead.
*/
class DEPRECATED() MITKLEGACYIO_EXPORT vtkPointSetXMLParser : public vtkXMLParser
{
public:
vtkTypeMacro(vtkPointSetXMLParser, vtkXMLParser);
static vtkPointSetXMLParser *New();
typedef mitk::PointSet PointSetType;
typedef std::stack<std::string> ParseStack;
typedef std::list<PointSetType::Pointer> PointSetList;
typedef PointSetType::DataType::PointIdentifier PointIdentifier;
typedef PointSetType::PointType PointType;
int InitializeParser() override;
int CleanupParser() override;
/**
* Handler function which is called, when a new xml start-tag
* has been parsed.
*/
void StartElement(const char *name, const char **atts) override;
/**
* Handler function which is called, when a xml end-tag
* has been parsed.
*/
void EndElement(const char *name) override;
/**
- * Handler function which is called, if characted data has been
+ * Handler function which is called, if character data has been
* parsed by expat.
* @param inData a char array containing the parsed string data
* @param inLength the length of the parsed data string.
*/
void CharacterDataHandler(const char *inData, int inLength) override;
/**
* Converts the given data to mitk::ScalarType.
*/
virtual mitk::ScalarType ParseScalarType(const std::string &data);
/**
* Converts the given data to an PointIdentifier
*/
virtual PointIdentifier ParsePointIdentifier(const std::string &data);
/**
* @returns the list of point sets which have been read from file.
* NOTE: your have to call the Parse() function, before this function.
*/
virtual PointSetList GetParsedPointSets();
protected:
vtkPointSetXMLParser();
~vtkPointSetXMLParser() override;
/**
* A stack containing the parsed start-tags.
* If an end tag is encountered, it is matched with the
* top element of the stack.
*/
ParseStack m_ParseStack;
/**
* Contains the parsed point sets.
*/
PointSetList m_PointSetList;
/**
* The current point set which is processed
* by the parser.
*/
PointSetType::Pointer m_CurrentPointSet;
/**
* The current point which is processed
* by the parser.
*/
PointType m_CurrentPoint;
std::string m_CurId;
std::string m_CurXString;
std::string m_CurYString;
std::string m_CurZString;
/**
* The current point id which is processed
* by the parser.
*/
PointIdentifier m_CurrentPointId;
std::locale m_PreviousLocale;
};
}
#endif // _VTK_POINT_SET_XML_READER__H_
diff --git a/Modules/Log/include/mitkLog.h b/Modules/Log/include/mitkLog.h
index 37b44c2ca7..3f2a485589 100644
--- a/Modules/Log/include/mitkLog.h
+++ b/Modules/Log/include/mitkLog.h
@@ -1,220 +1,220 @@
/*============================================================================
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 mitkLog_h
#define mitkLog_h
#include <mitkLogBackendBase.h>
#include <sstream>
#include <MitkLogExports.h>
#ifndef MITKLOG_MODULENAME
# if defined(US_MODULE_NAME)
# define MITKLOG_STR_(x) #x
# define MITKLOG_STR(x) MITKLOG_STR_(x)
# define MITKLOG_MODULENAME MITKLOG_STR(US_MODULE_NAME)
# else
# define MITKLOG_MODULENAME "n/a"
# endif
#endif
namespace mitk
{
/** \brief Register a backend in the MITK log mechanism.
*
* If a backend is registered here, all log messages are relayed to this backend through the method ProcessMessage.
* If no backend is registered, the default backend is used.
*/
void MITKLOG_EXPORT RegisterBackend(LogBackendBase* backend);
/** \brief Unregister a backend.
*/
void MITKLOG_EXPORT UnregisterBackend(LogBackendBase* backend);
/** \brief Distribute the given message to all registered backends.
*
* Should only be called by PseudoLogStream objects.
*/
void MITKLOG_EXPORT DistributeToBackends(LogMessage& message);
/** \brief Enable the output of a backend.
*/
void MITKLOG_EXPORT EnableBackends(LogBackendBase::OutputType type);
/** \brief Disable the output of a backend.
*/
void MITKLOG_EXPORT DisableBackends(LogBackendBase::OutputType type);
- /** \brief Check wether the output of this backend is enabled.
+ /** \brief Check whether the output of this backend is enabled.
*/
bool MITKLOG_EXPORT IsBackendEnabled(LogBackendBase::OutputType type);
/** \brief Simulates a std::cout stream.
*
* Should only be used by the macros defined in the file mitkLog.h.
*/
class MITKLOG_EXPORT PseudoLogStream
{
public:
PseudoLogStream(LogLevel level, const std::string& filePath, int lineNumber, const std::string& functionName)
: m_Disabled(false),
m_Message(level, filePath, lineNumber, functionName),
m_Stream(std::stringstream::out)
{
}
/** \brief The encapsulated message is written to the backend.
*/
~PseudoLogStream()
{
if (!m_Disabled)
{
m_Message.Message = m_Stream.str();
m_Message.ModuleName = MITKLOG_MODULENAME;
DistributeToBackends(m_Message);
}
}
template <class T>
PseudoLogStream& operator<<(const T& data)
{
if (!m_Disabled)
{
std::locale C("C");
std::locale originalLocale = m_Stream.getloc();
m_Stream.imbue(C);
m_Stream << data;
m_Stream.imbue(originalLocale);
}
return *this;
}
template <class T>
PseudoLogStream& operator<<(T& data)
{
if (!m_Disabled)
{
std::locale C("C");
std::locale originalLocale = m_Stream.getloc();
m_Stream.imbue(C);
m_Stream << data;
m_Stream.imbue(originalLocale);
}
return *this;
}
PseudoLogStream& operator<<(std::ostream& (*func)(std::ostream&))
{
if (!m_Disabled)
{
std::locale C("C");
std::locale originalLocale = m_Stream.getloc();
m_Stream.imbue(C);
m_Stream << func;
m_Stream.imbue(originalLocale);
}
return *this;
}
/** \brief Sets the category of this PseudoLogStream object.
*
- * If there is already a category it is appended, seperated by a dot.
+ * If there is already a category it is appended, separated by a dot.
*/
PseudoLogStream& operator()(const std::string& category)
{
if (!m_Disabled)
{
if (m_Message.Category.length())
m_Message.Category += ".";
m_Message.Category += category;
}
return *this;
}
/** \brief Enables/disables the PseudoLogStream.
*
* If set to false, parsing and output is suppressed.
*/
PseudoLogStream& operator()(bool enabled)
{
m_Disabled |= !enabled;
return *this;
}
protected:
bool m_Disabled;
LogMessage m_Message;
std::stringstream m_Stream;
};
/**
* \brief Simulates a std::cout stream but does nothing.
*
* Should only be used by the macros defined in the file mitkLog.h.
*/
class MITKLOG_EXPORT NullLogStream
{
public:
template <class T>
NullLogStream& operator<<(const T&)
{
return *this;
}
template <class T>
NullLogStream& operator<<(T&)
{
return *this;
}
NullLogStream& operator<<(std::ostream &(*)(std::ostream &))
{
return *this;
}
NullLogStream& operator()(const char*)
{
return *this;
}
NullLogStream& operator()(bool)
{
return *this;
}
};
}
#define MITK_INFO mitk::PseudoLogStream(mitk::LogLevel::Info, __FILE__, __LINE__, __FUNCTION__)
#define MITK_WARN mitk::PseudoLogStream(mitk::LogLevel::Warn, __FILE__, __LINE__, __FUNCTION__)
#define MITK_ERROR mitk::PseudoLogStream(mitk::LogLevel::Error, __FILE__, __LINE__, __FUNCTION__)
#define MITK_FATAL mitk::PseudoLogStream(mitk::LogLevel::Fatal, __FILE__, __LINE__, __FUNCTION__)
#ifdef MITK_ENABLE_DEBUG_MESSAGES
#define MITK_DEBUG mitk::PseudoLogStream(mitk::LogLevel::Debug, __FILE__, __LINE__, __FUNCTION__)
#else
#define MITK_DEBUG true ? mitk::NullLogStream() : mitk::NullLogStream()
#endif
#endif
diff --git a/Modules/MapperExt/include/vtkMaskedGlyph2D.h b/Modules/MapperExt/include/vtkMaskedGlyph2D.h
index 56b9439c45..a79506c5bd 100644
--- a/Modules/MapperExt/include/vtkMaskedGlyph2D.h
+++ b/Modules/MapperExt/include/vtkMaskedGlyph2D.h
@@ -1,105 +1,105 @@
/*============================================================================
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 __vtkMaskedGlyph2D_h
#define __vtkMaskedGlyph2D_h
#include "MitkMapperExtExports.h"
#include "mitkCommon.h"
#include "vtkGlyph2D.h"
class vtkMaskPoints;
/**
* This class masked points of the input data set and glyphs
- * only the selected poitns. Points may be selected either by
+ * only the selected points. Points may be selected either by
* random or by ratio.
* Additionally, this class allows to set the InputScalars,
* InputVectors and InputNormals by their field name in the
* input dataset.
*/
class MITKMAPPEREXT_EXPORT vtkMaskedGlyph2D : public vtkGlyph2D
{
public:
vtkTypeMacro(vtkMaskedGlyph2D, vtkGlyph2D);
void PrintSelf(ostream &os, vtkIndent indent) override;
/**
* Constructor
*/
static vtkMaskedGlyph2D *New();
/**
* Limit the number of points to glyph
*/
vtkSetMacro(MaximumNumberOfPoints, int);
vtkGetMacro(MaximumNumberOfPoints, int);
/**
* Set the input to this filter.
*/
virtual void SetInput(vtkDataSet *input);
/**
* Set/get whether to mask points
*/
vtkSetMacro(UseMaskPoints, int);
vtkGetMacro(UseMaskPoints, int);
/**
* Set/get flag to cause randomization of which points to mask.
*/
void SetRandomMode(int mode);
int GetRandomMode();
///**
// * If you want to use an arbitrary scalars array, then set its name here.
// * By default this in nullptr and the filter will use the active scalar array.
// */
// vtkGetStringMacro(InputScalarsSelection);
// void SelectInputScalars(const char *fieldName)
// {this->SetInputScalarsSelection(fieldName);}
///**
// * If you want to use an arbitrary vectors array, then set its name here.
// * By default this in nullptr and the filter will use the active vector array.
// */
// vtkGetStringMacro(InputVectorsSelection);
// void SelectInputVectors(const char *fieldName)
// {this->SetInputVectorsSelection(fieldName);}
///**
// * If you want to use an arbitrary normals array, then set its name here.
// * By default this in nullptr and the filter will use the active normal array.
// */
// vtkGetStringMacro(InputNormalsSelection);
// void SelectInputNormals(const char *fieldName)
// {this->SetInputNormalsSelection(fieldName);}
protected:
vtkMaskedGlyph2D();
~vtkMaskedGlyph2D() override;
int RequestData(vtkInformation *info,
vtkInformationVector **inInfoVec,
vtkInformationVector *outInfoVec) override;
vtkMaskPoints *MaskPoints;
int MaximumNumberOfPoints;
int UseMaskPoints;
private:
vtkMaskedGlyph2D(const vtkMaskedGlyph2D &); // Not implemented.
void operator=(const vtkMaskedGlyph2D &); // Not implemented.
};
#endif
diff --git a/Modules/MapperExt/include/vtkMaskedGlyph3D.h b/Modules/MapperExt/include/vtkMaskedGlyph3D.h
index c9ea2d6a5f..8d77dd08be 100644
--- a/Modules/MapperExt/include/vtkMaskedGlyph3D.h
+++ b/Modules/MapperExt/include/vtkMaskedGlyph3D.h
@@ -1,84 +1,84 @@
/*============================================================================
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 __vtkMaskedGlyph3D_h
#define __vtkMaskedGlyph3D_h
#include "MitkMapperExtExports.h"
#include "vtkGlyph3D.h"
#include "mitkCommon.h"
class vtkMaskPoints;
/**
* This class masked points of the input data set and glyphs
- * only the selected poitns. Points may be selected either by
+ * only the selected points. Points may be selected either by
* random or by ratio.
* Additionally, this class allows to set the InputScalars,
* InputVectors and InputNormals by their field name in the
* input dataset.
*/
class MITKMAPPEREXT_EXPORT vtkMaskedGlyph3D : public vtkGlyph3D
{
public:
vtkTypeMacro(vtkMaskedGlyph3D, vtkGlyph3D);
void PrintSelf(ostream &os, vtkIndent indent) override;
/**
* Constructor
*/
static vtkMaskedGlyph3D *New();
/**
* Limit the number of points to glyph
*/
vtkSetMacro(MaximumNumberOfPoints, int);
vtkGetMacro(MaximumNumberOfPoints, int);
/**
* Set the input to this filter.
*/
virtual void SetInput(vtkDataSet *input);
/**
* Set/get whether to mask points
*/
vtkSetMacro(UseMaskPoints, int);
vtkGetMacro(UseMaskPoints, int);
/**
* Set/get flag to cause randomization of which points to mask.
*/
void SetRandomMode(int mode);
int GetRandomMode();
void SetInputConnection(vtkAlgorithmOutput *input) override;
using vtkGlyph3D::SetInputConnection;
protected:
vtkMaskedGlyph3D();
~vtkMaskedGlyph3D() override;
int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *) override;
vtkMaskPoints *MaskPoints;
int MaximumNumberOfPoints;
int UseMaskPoints;
private:
vtkMaskedGlyph3D(const vtkMaskedGlyph3D &); // Not implemented.
void operator=(const vtkMaskedGlyph3D &); // Not implemented.
};
#endif
diff --git a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp
index a4db90ea37..c4f97e2d1d 100644
--- a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp
+++ b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp
@@ -1,555 +1,555 @@
/*============================================================================
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 "mitkUnstructuredGridMapper2D.h"
#include <mitkGL.h>
#include "mitkAbstractTransformGeometry.h"
#include "mitkBaseRenderer.h"
#include "mitkColorProperty.h"
#include "mitkPlaneGeometry.h"
#include "mitkProperties.h"
#include "mitkTransferFunction.h"
#include "mitkTransferFunctionProperty.h"
#include "mitkUnstructuredGrid.h"
#include "mitkVtkMapper3D.h"
#include "mitkVtkScalarModeProperty.h"
#include <vtkAbstractMapper3D.h>
#include <vtkAbstractVolumeMapper.h>
#include <vtkAssembly.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkColorTransferFunction.h>
#include <vtkLinearTransform.h>
#include <vtkLookupTable.h>
#include <vtkPiecewiseFunction.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkProp3DCollection.h>
#include <vtkScalarsToColors.h>
#include <vtkUnstructuredGrid.h>
#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
#include "vtkPointSetSlicer.h"
void mitk::UnstructuredGridMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer);
bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode());
if (needGenerateData)
{
ls->UpdateGenerateDataTime();
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return;
if (!node->GetProperty(m_ScalarMode, "scalar mode"))
{
m_ScalarMode = mitk::VtkScalarModeProperty::New(0);
}
if (!node->GetProperty(m_ScalarVisibility, "scalar visibility"))
{
m_ScalarVisibility = mitk::BoolProperty::New(true);
}
if (!node->GetProperty(m_Outline, "outline polygons"))
{
m_Outline = mitk::BoolProperty::New(false);
}
if (!node->GetProperty(m_Color, "color"))
{
m_Color = mitk::ColorProperty::New(1.0f, 1.0f, 1.0f);
}
if (!node->GetProperty(m_LineWidth, "line width"))
{
m_LineWidth = mitk::IntProperty::New(1);
}
}
mitk::BaseData::Pointer input = GetDataNode()->GetData();
assert(input);
input->Update();
if (m_VtkPointSet)
m_VtkPointSet->UnRegister(nullptr);
m_VtkPointSet = this->GetVtkPointSet(renderer, this->GetTimestep());
assert(m_VtkPointSet);
m_VtkPointSet->Register(nullptr);
if (m_ScalarVisibility->GetValue())
{
mitk::DataNode::ConstPointer node = this->GetDataNode();
mitk::TransferFunctionProperty::Pointer transferFuncProp;
node->GetProperty(transferFuncProp, "TransferFunction", renderer);
if (transferFuncProp.IsNotNull())
{
mitk::TransferFunction::Pointer tf = transferFuncProp->GetValue();
if (m_ScalarsToColors)
m_ScalarsToColors->UnRegister(nullptr);
m_ScalarsToColors = static_cast<vtkScalarsToColors *>(tf->GetColorTransferFunction());
m_ScalarsToColors->Register(nullptr);
if (m_ScalarsToOpacity)
m_ScalarsToOpacity->UnRegister(nullptr);
m_ScalarsToOpacity = tf->GetScalarOpacityFunction();
m_ScalarsToOpacity->Register(nullptr);
}
else
{
if (m_ScalarsToColors)
m_ScalarsToColors->UnRegister(nullptr);
m_ScalarsToColors = this->GetVtkLUT(renderer);
assert(m_ScalarsToColors);
m_ScalarsToColors->Register(nullptr);
float opacity;
node->GetOpacity(opacity, renderer);
if (m_ScalarsToOpacity)
m_ScalarsToOpacity->UnRegister(nullptr);
m_ScalarsToOpacity = vtkPiecewiseFunction::New();
double range[2];
m_VtkPointSet->GetScalarRange(range);
m_ScalarsToOpacity->AddSegment(range[0], opacity, range[1], opacity);
}
}
}
void mitk::UnstructuredGridMapper2D::Paint(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform();
vtkLinearTransform *inversetransform = vtktransform->GetLinearInverse();
PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry.GetPointer());
Point3D point;
Vector3D normal;
if (worldPlaneGeometry.IsNotNull())
{
// set up vtkPlane according to worldGeometry
point = worldPlaneGeometry->GetOrigin();
normal = worldPlaneGeometry->GetNormal();
normal.Normalize();
m_Plane->SetTransform((vtkAbstractTransform *)nullptr);
}
else
{
//@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "plane plane" into a "curved
//plane"?
return;
AbstractTransformGeometry::ConstPointer worldAbstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(renderer->GetCurrentWorldPlaneGeometry());
if (worldAbstractGeometry.IsNotNull())
{
// set up vtkPlane according to worldGeometry
point = worldAbstractGeometry->GetParametricBoundingBox()->GetMinimum();
FillVector3D(normal, 0, 0, 1);
m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse());
}
else
return;
}
double vp[3], vnormal[3];
vnl2vtk(point.GetVnlVector(), vp);
vnl2vtk(normal.GetVnlVector(), vnormal);
// normally, we would need to transform the surface and cut the transformed surface with the cutter.
// This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead.
//@todo It probably does not work for scaling operations yet:scaling operations have to be
- // dealed with after the cut is performed by scaling the contour.
+ // dealt with after the cut is performed by scaling the contour.
inversetransform->TransformPoint(vp, vp);
inversetransform->TransformNormalAtPoint(vp, vnormal, vnormal);
m_Plane->SetOrigin(vp);
m_Plane->SetNormal(vnormal);
// set data into cutter
m_Slicer->SetInputData(m_VtkPointSet);
// m_Cutter->GenerateCutScalarsOff();
// m_Cutter->SetSortByToSortByCell();
// calculate the cut
m_Slicer->Update();
// apply color and opacity read from the PropertyList
ApplyColorAndOpacityProperties(renderer);
// traverse the cut contour
vtkPolyData *contour = m_Slicer->GetOutput();
vtkPoints *vpoints = contour->GetPoints();
vtkCellArray *vlines = contour->GetLines();
vtkCellArray *vpolys = contour->GetPolys();
vtkPointData *vpointdata = contour->GetPointData();
vtkDataArray *vscalars = vpointdata->GetScalars();
vtkCellData *vcelldata = contour->GetCellData();
vtkDataArray *vcellscalars = vcelldata->GetScalars();
const int numberOfLines = contour->GetNumberOfLines();
const int numberOfPolys = contour->GetNumberOfPolys();
const bool useCellData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT ||
m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA;
const bool usePointData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA;
Point3D p;
Point2D p2d;
vlines->InitTraversal();
vpolys->InitTraversal();
mitk::Color outlineColor = m_Color->GetColor();
glLineWidth((float)m_LineWidth->GetValue());
for (int i = 0; i < numberOfLines; ++i)
{
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
vlines->GetNextCell(cellSize, cell);
float rgba[4] = {outlineColor[0], outlineColor[1], outlineColor[2], 1.0f};
if (m_ScalarVisibility->GetValue() && vcellscalars)
{
if (useCellData)
{ // color each cell according to cell data
double scalar = vcellscalars->GetComponent(i, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
else if (usePointData)
{
double scalar = vscalars->GetComponent(i, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
}
glColor4fv(rgba);
glBegin(GL_LINE_LOOP);
for (int j = 0; j < cellSize; ++j)
{
vpoints->GetPoint(cell[j], vp);
// take transformation via vtktransform into account
vtktransform->TransformPoint(vp, vp);
vtk2itk(vp, p);
// convert 3D point (in mm) to display coordinates (units )
renderer->WorldToDisplay(p, p2d);
// convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left )
// p2d[1]=toGL-p2d[1];
// add the current vertex to the line
glVertex2f(p2d[0], p2d[1]);
}
glEnd();
}
bool polyOutline = m_Outline->GetValue();
bool scalarVisibility = m_ScalarVisibility->GetValue();
// cache the transformed points
// a fixed size array is way faster than 'new'
// slices through 3d cells usually do not generated
// polygons with more than 6 vertices
const int maxPolySize = 10;
auto *cachedPoints = new Point2D[maxPolySize * numberOfPolys];
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// only draw polygons if there are cell scalars
// or the outline property is set to true
if (scalarVisibility && vcellscalars)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
for (int i = 0; i < numberOfPolys; ++i)
{
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
vpolys->GetNextCell(cellSize, cell);
float rgba[4] = {1.0f, 1.0f, 1.0f, 0};
if (scalarVisibility && vcellscalars)
{
if (useCellData)
{ // color each cell according to cell data
double scalar = vcellscalars->GetComponent(i + numberOfLines, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
else if (usePointData)
{
double scalar = vscalars->GetComponent(i, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
}
glColor4fv(rgba);
glBegin(GL_POLYGON);
for (int j = 0; j < cellSize; ++j)
{
vpoints->GetPoint(cell[j], vp);
// take transformation via vtktransform into account
vtktransform->TransformPoint(vp, vp);
vtk2itk(vp, p);
// convert 3D point (in mm) to display coordinates (units )
renderer->WorldToDisplay(p, p2d);
// convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left )
// p2d[1]=toGL-p2d[1];
cachedPoints[i * 10 + j][0] = p2d[0];
cachedPoints[i * 10 + j][1] = p2d[1];
// add the current vertex to the line
glVertex2f(p2d[0], p2d[1]);
}
glEnd();
}
if (polyOutline)
{
vpolys->InitTraversal();
glColor4f(outlineColor[0], outlineColor[1], outlineColor[2], 1.0f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
for (int i = 0; i < numberOfPolys; ++i)
{
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
vpolys->GetNextCell(cellSize, cell);
glBegin(GL_POLYGON);
// glPolygonOffset(1.0, 1.0);
for (int j = 0; j < cellSize; ++j)
{
// add the current vertex to the line
glVertex2f(cachedPoints[i * 10 + j][0], cachedPoints[i * 10 + j][1]);
}
glEnd();
}
}
}
glDisable(GL_BLEND);
delete[] cachedPoints;
}
vtkAbstractMapper3D *mitk::UnstructuredGridMapper2D::GetVtkAbstractMapper3D(mitk::BaseRenderer *renderer)
{
// MITK_INFO << "GETVTKABSTRACTMAPPER3D\n";
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return nullptr;
mitk::VtkMapper::Pointer mitkMapper = dynamic_cast<mitk::VtkMapper *>(node->GetMapper(2));
if (mitkMapper.IsNull())
{
return nullptr;
}
mitkMapper->Update(renderer);
auto *assembly = dynamic_cast<vtkAssembly *>(mitkMapper->GetVtkProp(renderer));
if (assembly)
{
vtkProp3DCollection *collection = assembly->GetParts();
collection->InitTraversal();
vtkProp3D *prop3d = nullptr;
do
{
prop3d = collection->GetNextProp3D();
auto *actor = dynamic_cast<vtkActor *>(prop3d);
if (actor)
{
return dynamic_cast<vtkAbstractMapper3D *>(actor->GetMapper());
}
auto *volume = dynamic_cast<vtkVolume *>(prop3d);
if (volume)
{
return dynamic_cast<vtkAbstractMapper3D *>(volume->GetMapper());
}
} while (prop3d != collection->GetLastProp3D());
}
else
{
auto *actor = dynamic_cast<vtkActor *>(mitkMapper->GetVtkProp(renderer));
if (actor)
{
return dynamic_cast<vtkAbstractMapper3D *>(actor->GetMapper());
}
auto *volume = dynamic_cast<vtkVolume *>(mitkMapper->GetVtkProp(renderer));
if (volume)
{
return dynamic_cast<vtkAbstractMapper3D *>(volume->GetMapper());
}
}
return nullptr;
}
vtkPointSet *mitk::UnstructuredGridMapper2D::GetVtkPointSet(mitk::BaseRenderer *renderer, int time)
{
// MITK_INFO << "GETVTKPOINTSET\n";
vtkAbstractMapper3D *abstractMapper = GetVtkAbstractMapper3D(renderer);
if (abstractMapper == nullptr)
{
// try to get data from the node
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return nullptr;
mitk::BaseData::Pointer data = node->GetData();
mitk::UnstructuredGrid::Pointer grid = dynamic_cast<mitk::UnstructuredGrid *>(data.GetPointer());
if (!grid.IsNull())
return static_cast<vtkPointSet *>(grid->GetVtkUnstructuredGrid(time));
return nullptr;
}
else
{
auto *mapper = dynamic_cast<vtkMapper *>(abstractMapper);
if (mapper)
{
return dynamic_cast<vtkPointSet *>(mapper->GetInput());
}
auto *volMapper = dynamic_cast<vtkAbstractVolumeMapper *>(abstractMapper);
if (volMapper)
{
return dynamic_cast<vtkPointSet *>(volMapper->GetDataSetInput());
}
}
return nullptr;
}
vtkScalarsToColors *mitk::UnstructuredGridMapper2D::GetVtkLUT(mitk::BaseRenderer *renderer)
{
// MITK_INFO << "GETVTKLUT\n";
auto *mapper = dynamic_cast<vtkMapper *>(GetVtkAbstractMapper3D(renderer));
if (mapper)
return mapper->GetLookupTable();
else
{
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return nullptr;
mitk::VtkMapper::Pointer mitkMapper = dynamic_cast<mitk::VtkMapper *>(node->GetMapper(2));
if (mitkMapper.IsNull())
{
// MITK_INFO << "mitkMapper is null\n";
return nullptr;
}
mitkMapper->Update(renderer);
auto *volume = dynamic_cast<vtkVolume *>(mitkMapper->GetVtkProp(renderer));
if (volume)
{
// MITK_INFO << "found volume prop\n";
return static_cast<vtkScalarsToColors *>(volume->GetProperty()->GetRGBTransferFunction());
}
auto *assembly = dynamic_cast<vtkAssembly *>(mitkMapper->GetVtkProp(renderer));
if (assembly)
{
// MITK_INFO << "found assembly prop\n";
mitk::TransferFunctionProperty::Pointer transferFuncProp;
node->GetProperty(transferFuncProp, "TransferFunction", nullptr);
if (transferFuncProp.IsNotNull())
{
MITK_INFO << "return colortransferfunction\n";
return static_cast<vtkScalarsToColors *>(transferFuncProp->GetValue()->GetColorTransferFunction());
}
}
return nullptr;
}
}
bool mitk::UnstructuredGridMapper2D::IsConvertibleToVtkPointSet(mitk::BaseRenderer *renderer)
{
return (GetVtkPointSet(renderer, this->GetTimestep()) != nullptr);
}
mitk::UnstructuredGridMapper2D::UnstructuredGridMapper2D()
{
m_Plane = vtkPlane::New();
m_Slicer = vtkPointSetSlicer::New();
m_Slicer->SetSlicePlane(m_Plane);
m_ScalarsToColors = nullptr;
m_ScalarsToOpacity = nullptr;
m_VtkPointSet = nullptr;
// m_LUT = vtkLookupTable::New();
// m_LUT->SetTableRange( 0, 255 );
// m_LUT->SetNumberOfColors( 255 );
// m_LUT->SetRampToLinear ();
// m_LUT->Build();
}
mitk::UnstructuredGridMapper2D::~UnstructuredGridMapper2D()
{
m_Slicer->Delete();
m_Plane->Delete();
if (m_ScalarsToOpacity != nullptr)
m_ScalarsToOpacity->UnRegister(nullptr);
if (m_ScalarsToColors != nullptr)
m_ScalarsToColors->UnRegister(nullptr);
if (m_VtkPointSet != nullptr)
m_VtkPointSet->UnRegister(nullptr);
}
diff --git a/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp b/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp
index 0d13d2960b..e4e8a0aba7 100644
--- a/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp
+++ b/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp
@@ -1,527 +1,527 @@
/*============================================================================
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 "mitkVectorImageMapper2D.h"
// vtk related includes
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkCutter.h>
#include <vtkDataArray.h>
#include <vtkDataObject.h>
#include <vtkDataSetWriter.h>
#include <vtkFloatArray.h>
#include <vtkGlyph2D.h>
#include <vtkGlyphSource2D.h>
#include <vtkImageData.h>
#include <vtkImageReslice.h>
#include <vtkIndent.h>
#include <vtkLinearTransform.h>
#include <vtkLookupTable.h>
#include <vtkLookupTable.h>
#include <vtkMaskedGlyph2D.h>
#include <vtkMaskedGlyph3D.h>
#include <vtkMath.h>
#include <vtkMatrix4x4.h>
#include <vtkMatrixToLinearTransform.h>
#include <vtkPlane.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkScalarsToColors.h>
#include <vtkScalarsToColors.h>
#include <vtkTransform.h>
#include <fstream>
// mitk related includes
#include "mitkAbstractTransformGeometry.h"
#include "mitkBaseRenderer.h"
#include "mitkColorProperty.h"
#include "mitkGL.h"
#include "mitkProperties.h"
#include <mitkLookupTableProperty.h>
const mitk::Image *mitk::VectorImageMapper2D::GetInput(void)
{
if (m_Image.IsNotNull())
return m_Image;
else
return dynamic_cast<const mitk::Image *>(GetDataNode()->GetData());
}
void mitk::VectorImageMapper2D::Paint(mitk::BaseRenderer *renderer)
{
// std::cout << "2d vector mapping..." << std::endl;
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
mitk::Image::Pointer input = const_cast<mitk::Image *>(this->GetInput());
if (input.IsNull())
return;
vtkImageData *vtkImage = input->GetVtkImageData(this->GetCurrentTimeStep(input, renderer));
//
// set up the cutter orientation according to the current geometry of
// the renderers plane
//
Point3D point;
Vector3D normal;
PlaneGeometry::ConstPointer worldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (worldPlaneGeometry.IsNotNull())
{
// set up vtkPlane according to worldGeometry
point = worldPlaneGeometry->GetOrigin();
normal = worldPlaneGeometry->GetNormal();
normal.Normalize();
m_Plane->SetTransform((vtkAbstractTransform *)nullptr);
}
else
{
itkWarningMacro(<< "worldPlaneGeometry is nullptr!");
return;
}
double vp[3], vp_slice[3], vnormal[3];
vnl2vtk(point.GetVnlVector(), vp);
vnl2vtk(normal.GetVnlVector(), vnormal);
// std::cout << "Origin: " << vp[0] <<" "<< vp[1] <<" "<< vp[2] << std::endl;
// std::cout << "Normal: " << vnormal[0] <<" "<< vnormal[1] <<" "<< vnormal[2] << std::endl;
// normally, we would need to transform the surface and cut the transformed surface with the cutter.
// This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead.
//@todo It probably does not work for scaling operations yet:scaling operations have to be
- // dealed with after the cut is performed by scaling the contour.
+ // dealt with after the cut is performed by scaling the contour.
vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform();
vtkTransform *world2vtk = vtkTransform::New();
world2vtk->Identity();
world2vtk->Concatenate(vtktransform->GetLinearInverse());
double myscale[3];
world2vtk->GetScale(myscale);
world2vtk->PostMultiply();
world2vtk->Scale(1 / myscale[0], 1 / myscale[1], 1 / myscale[2]);
world2vtk->TransformPoint(vp, vp);
world2vtk->TransformNormalAtPoint(vp, vnormal, vnormal);
world2vtk->Delete();
// vtk works in axis align coords
// thus the normal also must be axis align, since
// we do not allow arbitrary cutting through volume
//
// vnormal should already be axis align, but in order
// to get rid of precision effects, we set the two smaller
// components to zero here
int dims[3];
vtkImage->GetDimensions(dims);
double spac[3];
vtkImage->GetSpacing(spac);
vp_slice[0] = vp[0];
vp_slice[1] = vp[1];
vp_slice[2] = vp[2];
if (fabs(vnormal[0]) > fabs(vnormal[1]) && fabs(vnormal[0]) > fabs(vnormal[2]))
{
if (fabs(vp_slice[0] / spac[0]) < 0.4)
vp_slice[0] = 0.4 * spac[0];
if (fabs(vp_slice[0] / spac[0]) > (dims[0] - 1) - 0.4)
vp_slice[0] = ((dims[0] - 1) - 0.4) * spac[0];
vnormal[1] = 0;
vnormal[2] = 0;
}
if (fabs(vnormal[1]) > fabs(vnormal[0]) && fabs(vnormal[1]) > fabs(vnormal[2]))
{
if (fabs(vp_slice[1] / spac[1]) < 0.4)
vp_slice[1] = 0.4 * spac[1];
if (fabs(vp_slice[1] / spac[1]) > (dims[1] - 1) - 0.4)
vp_slice[1] = ((dims[1] - 1) - 0.4) * spac[1];
vnormal[0] = 0;
vnormal[2] = 0;
}
if (fabs(vnormal[2]) > fabs(vnormal[1]) && fabs(vnormal[2]) > fabs(vnormal[0]))
{
if (fabs(vp_slice[2] / spac[2]) < 0.4)
vp_slice[2] = 0.4 * spac[2];
if (fabs(vp_slice[2] / spac[2]) > (dims[2] - 1) - 0.4)
vp_slice[2] = ((dims[2] - 1) - 0.4) * spac[2];
vnormal[0] = 0;
vnormal[1] = 0;
}
m_Plane->SetOrigin(vp_slice);
m_Plane->SetNormal(vnormal);
vtkPolyData *cuttedPlane;
if (!((dims[0] == 1 && vnormal[0] != 0) || (dims[1] == 1 && vnormal[1] != 0) || (dims[2] == 1 && vnormal[2] != 0)))
{
m_Cutter->SetCutFunction(m_Plane);
m_Cutter->SetInputData(vtkImage);
m_Cutter->GenerateCutScalarsOff(); //!
m_Cutter->Update();
cuttedPlane = m_Cutter->GetOutput();
}
else
{
// cutting of a 2D-Volume does not work,
// so we have to build up our own polydata object
cuttedPlane = vtkPolyData::New();
vtkPoints *points = vtkPoints::New();
points->SetNumberOfPoints(vtkImage->GetNumberOfPoints());
for (int i = 0; i < vtkImage->GetNumberOfPoints(); i++)
points->SetPoint(i, vtkImage->GetPoint(i));
cuttedPlane->SetPoints(points);
vtkFloatArray *pointdata = vtkFloatArray::New();
int comps = vtkImage->GetPointData()->GetScalars()->GetNumberOfComponents();
pointdata->SetNumberOfComponents(comps);
int tuples = vtkImage->GetPointData()->GetScalars()->GetNumberOfTuples();
pointdata->SetNumberOfTuples(tuples);
for (int i = 0; i < tuples; i++)
pointdata->SetTuple(i, vtkImage->GetPointData()->GetScalars()->GetTuple(i));
pointdata->SetName("vector");
cuttedPlane->GetPointData()->AddArray(pointdata);
}
if (cuttedPlane->GetNumberOfPoints() != 0)
{
//
// make sure, that we have point data with more than 1 component (as vectors)
//
vtkPointData *pointData = cuttedPlane->GetPointData();
if (pointData == nullptr)
{
itkWarningMacro(<< "no point data associated with cutters result!");
return;
}
if (pointData->GetNumberOfArrays() == 0)
{
itkWarningMacro(<< "point data returned by cutter doesn't have any arrays associated!");
return;
}
else if (pointData->GetArray(0)->GetNumberOfComponents() <= 1)
{
itkWarningMacro(<< "number of components <= 1!");
return;
}
else if (pointData->GetArrayName(0) == nullptr)
{
pointData->GetArray(0)->SetName("vector");
// std::cout << "array name = vectors now" << std::endl;
}
// std::cout << " projecting..."<< std::endl;
//
// constrain the vectors to lie on the plane, which means to remove the vector component,
// which is orthogonal to the plane.
//
vtkIdType numPoints, pointId;
numPoints = cuttedPlane->GetNumberOfPoints();
vtkDataArray *inVectors = cuttedPlane->GetPointData()->GetVectors("vector");
assert(inVectors != nullptr);
vtkFloatArray *vectorMagnitudes = vtkFloatArray::New();
vectorMagnitudes->SetName("vectorMagnitudes");
vectorMagnitudes->SetNumberOfComponents(1);
vectorMagnitudes->SetNumberOfValues(numPoints);
vectorMagnitudes->SetNumberOfTuples(numPoints);
double inVector[3], outVector[3], wnormal[3]; //, tmpVector[ 3 ], outVector[ 3 ];
double k = 0.0;
vnl2vtk(normal.GetVnlVector(), wnormal);
vtkMath::Normalize(wnormal);
bool normalizeVecs;
m_DataNode->GetBoolProperty("NormalizeVecs", normalizeVecs);
for (pointId = 0; pointId < numPoints; ++pointId)
{
inVectors->GetTuple(pointId, inVector);
if (normalizeVecs)
{
vnl_vector<double> tmp(3);
vtk2vnl(inVector, tmp);
tmp.normalize();
vnl2vtk(tmp, inVector);
}
k = vtkMath::Dot(wnormal, inVector);
// Remove non orthogonal component.
outVector[0] = inVector[0] - (wnormal[0] * k);
outVector[1] = inVector[1] - (wnormal[1] * k);
outVector[2] = inVector[2] - (wnormal[2] * k);
inVectors->SetTuple(pointId, outVector);
// ?? this was set to norm(inVector) before, but outVector made more sense to me
vectorMagnitudes->SetValue(pointId, vtkMath::Norm(outVector));
// std::cout << "method old: " << inVector[0] <<", " << inVector[1] << ", "<<inVector[2] << ", method new: " <<
// outVector[0] << ", "<< outVector[1] << ", "<< outVector[2] << std::endl;
}
pointData->AddArray(vectorMagnitudes);
pointData->CopyAllOn();
// pointData->PrintSelf(std::cout, vtkIndent(4));
// std::cout << " ...done!"<< std::endl;
// std::cout << " glyphing..."<< std::endl;
// call glyph2D to generate 2D glyphs for each of the
// vectors
vtkGlyphSource2D *glyphSource = vtkGlyphSource2D::New();
// glyphSource->SetGlyphTypeToDash();
glyphSource->DashOn();
// glyphSource->SetScale( 0.1 );
// glyphSource->SetScale2( .5 );
// glyphSource->SetCenter( 0.5, 0.5, 0.5 );
glyphSource->CrossOff();
// glyphSource->FilledOff();
// glyphSource->Update();
double spacing[3];
vtkImage->GetSpacing(spacing);
double min = spacing[0];
min = min > spacing[1] ? spacing[1] : min;
min = min > spacing[2] ? spacing[2] : min;
float scale = 1;
mitk::FloatProperty::Pointer mitkScaleProp =
dynamic_cast<mitk::FloatProperty *>(GetDataNode()->GetProperty("Scale"));
if (mitkScaleProp.IsNotNull())
{
scale = mitkScaleProp->GetValue();
}
vtkMaskedGlyph3D *glyphGenerator = vtkMaskedGlyph3D::New();
glyphGenerator->SetSourceData(glyphSource->GetOutput());
glyphGenerator->SetInput(cuttedPlane);
glyphGenerator->SetInputArrayToProcess(1, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "vector");
glyphGenerator->SetVectorModeToUseVector();
glyphGenerator->OrientOn();
glyphGenerator->SetScaleFactor(min * scale);
glyphGenerator->SetUseMaskPoints(true);
glyphGenerator->SetRandomMode(true);
glyphGenerator->SetMaximumNumberOfPoints(128 * 128);
glyphGenerator->Update();
/*
vtkLookupTable* vtkLut = nullptr;
mitk::LookupTableProperty::Pointer mitkLutProp =
dynamic_cast<mitk::LookupTableProperty*>(GetDataNode()->GetProperty("LookupTable"));
if (mitkLutProp.IsNotNull())
{
vtkLut = mitkLutProp->GetLookupTable()->GetVtkLookupTable();
}
*/
mitk::Color color;
mitk::ColorProperty::Pointer mitkColorProp =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("color"));
if (mitkColorProp.IsNotNull())
{
color = mitkColorProp->GetColor();
}
else
{
color.SetRed(0);
color.SetBlue(1);
color.SetGreen(0);
}
float lwidth = 1;
mitk::FloatProperty::Pointer mitkLWidthProp =
dynamic_cast<mitk::FloatProperty *>(GetDataNode()->GetProperty("LineWidth"));
if (mitkLWidthProp.IsNotNull())
{
lwidth = mitkLWidthProp->GetValue();
}
vtkTransform *trafo = vtkTransform::New();
trafo->Identity();
trafo->Concatenate(vtktransform);
trafo->PreMultiply();
double myscale[3];
trafo->GetScale(myscale);
trafo->Scale(1 / myscale[0], 1 / myscale[1], 1 / myscale[2]);
this->PaintCells(glyphGenerator->GetOutput(),
renderer->GetCurrentWorldPlaneGeometry(),
trafo,
renderer,
nullptr /*vtkLut*/,
color,
lwidth,
spacing);
vectorMagnitudes->Delete();
glyphSource->Delete();
glyphGenerator->Delete();
trafo->Delete();
}
else
{
std::cout << " no points cutted!" << std::endl;
}
// std::cout << "...done!" << std::endl;
}
void mitk::VectorImageMapper2D::PaintCells(vtkPolyData *glyphs,
const PlaneGeometry * /*worldGeometry*/,
vtkLinearTransform *vtktransform,
mitk::BaseRenderer *renderer,
vtkScalarsToColors *lut,
mitk::Color color,
float lwidth,
double *spacing)
{
vtkPoints *points = glyphs->GetPoints();
vtkPointData *vpointdata = glyphs->GetPointData();
vtkDataArray *vpointscalars = vpointdata->GetArray("vectorMagnitudes");
// vtkDataArray* vpointpositions = vpointdata->GetArray("pointPositions");
assert(vpointscalars != nullptr);
// std::cout << " Scalars range 2d:" << vpointscalars->GetRange()[0] << " " << vpointscalars->GetRange()[0] <<
// std::endl;
Point3D p;
Point2D p2d;
vtkIdList *idList;
vtkCell *cell;
double offset[3];
for (auto &elem : offset)
{
elem = 0;
}
vtkIdType numCells = glyphs->GetNumberOfCells();
for (vtkIdType cellId = 0; cellId < numCells; ++cellId)
{
double vp[3];
cell = glyphs->GetCell(cellId);
idList = cell->GetPointIds();
int numPoints = idList->GetNumberOfIds();
if (numPoints == 1)
{
// take transformation via vtktransform into account
double pos[3], vp_raster[3];
points->GetPoint(idList->GetId(0), vp);
vp_raster[0] = vtkMath::Round(vp[0] / spacing[0]) * spacing[0];
vp_raster[1] = vtkMath::Round(vp[1] / spacing[1]) * spacing[1];
vp_raster[2] = vtkMath::Round(vp[2] / spacing[2]) * spacing[2];
vtktransform->TransformPoint(vp_raster, pos);
offset[0] = pos[0] - vp[0];
offset[1] = pos[1] - vp[1];
offset[2] = pos[2] - vp[2];
}
else
{
glLineWidth(lwidth);
glBegin(GL_LINE_LOOP);
for (int pointNr = 0; pointNr < numPoints; ++pointNr)
{
points->GetPoint(idList->GetId(pointNr), vp);
vp[0] = vp[0] + offset[0];
vp[1] = vp[1] + offset[1];
vp[2] = vp[2] + offset[2];
double tmp[3];
vtktransform->TransformPoint(vp, tmp);
vtk2itk(vp, p);
// convert 3D point (in mm) to display coordinates (units )
renderer->WorldToDisplay(p, p2d);
if (lut != nullptr)
{
// color each point according to point data
double *color;
if (vpointscalars != nullptr)
{
vpointscalars->GetComponent(pointNr, 0);
color = lut->GetColor(vpointscalars->GetComponent(idList->GetId(pointNr), 0));
glColor3f(color[0], color[1], color[2]);
}
}
else
{
glColor3f(color.GetRed(), color.GetGreen(), color.GetBlue());
}
// std::cout << idList->GetId( pointNr )<< ": " << p2d[0]<< " "<< p2d[1] << std::endl;
// draw the line
glVertex2f(p2d[0], p2d[1]);
}
glEnd();
}
}
}
mitk::VectorImageMapper2D::VectorImageMapper2D()
{
m_LUT = nullptr;
m_Plane = vtkPlane::New();
m_Cutter = vtkCutter::New();
m_Cutter->SetCutFunction(m_Plane);
m_Cutter->GenerateValues(1, 0, 1);
}
mitk::VectorImageMapper2D::~VectorImageMapper2D()
{
if (m_LUT != nullptr)
m_LUT->Delete();
if (m_Plane != nullptr)
m_Plane->Delete();
if (m_Cutter != nullptr)
m_Cutter->Delete();
}
int mitk::VectorImageMapper2D::GetCurrentTimeStep(mitk::BaseData *data, mitk::BaseRenderer *renderer)
{
//
// get the TimeGeometry of the input object
//
const TimeGeometry *dataTimeGeometry = data->GetUpdatedTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0))
{
itkWarningMacro(<< "The given object is missing a mitk::TimeGeometry, or the number of time steps is 0!");
return 0;
}
//
// get the world time
//
ScalarType time = renderer->GetTime();
//
// convert the world time to time steps of the input object
//
int timestep = 0;
if (time > itk::NumericTraits<mitk::ScalarType>::NonpositiveMin())
timestep = dataTimeGeometry->TimePointToTimeStep(time);
if (dataTimeGeometry->IsValidTimeStep(timestep) == false)
{
itkWarningMacro(<< timestep << " is not a valid time of the given data object!");
return 0;
}
return timestep;
}
diff --git a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp
index c5cf757609..e1a36f4c10 100644
--- a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp
+++ b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp
@@ -1,279 +1,279 @@
/*============================================================================
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 <iostream>
#include <fstream>
#include <clocale>
#include "mapRegistration.h"
#include "mapRegistrationFileWriter.h"
#include "mapRegistrationFileReader.h"
#include "mapLazyFileFieldKernelLoader.h"
#include <mitkCustomMimeType.h>
#include <mitkIOMimeTypes.h>
#include <mitkLocaleSwitch.h>
#include "mitkMAPRegistrationWrapperIO.h"
#include "mitkMAPRegistrationWrapper.h"
namespace mitk
{
/** Helper class that allows to use an functor in multiple combinations of
* moving and target dimensions on a passed MAPRegistrationWrapper instance.\n
* DimHelperSub is used DimHelper to iterate in a row of the dimension
* combination matrix.
*/
template< unsigned int i, unsigned int j, template < unsigned int, unsigned int> class TFunctor>
class DimHelperSub
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data)
{
if (TFunctor<i,j>::Execute(obj, data))
{
return true;
}
return DimHelperSub<i,j-1,TFunctor>::Execute(obj, data);
}
};
/** Specialized template version of DimSubHelper that indicates the end
* of the row in the dimension combination matrix, thus does nothing.
*/
template< unsigned int i, template < unsigned int, unsigned int> class TFunctor>
class DimHelperSub<i,1,TFunctor >
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper*, const map::core::String&)
{
//just unwind. Go to the next "row" with DimHelper
return false;
}
};
/** Helper class that allows to use an functor in multiple combinations of
* moving and target dimensions on a passed MAPRegistrationWrapper instance.\n
* It is helpful if you want to ensure that all combinations are checked/touched
* (e.g. 3D 3D, 3D 2D, 2D 3D, 2D 2D) without generating a large switch yard.
- * Think of n*m matrix (indicating the posible combinations). DimHelper walks from
+ * Think of n*m matrix (indicating the possible combinations). DimHelper walks from
* one row to the next and uses DimHelperSub to iterate in a row.\n
* For every element of the matrix the functor is executed on the passed object.
*/
template< unsigned int i, unsigned int j, template < unsigned int, unsigned int> class TFunctor>
class DimHelper{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data = "")
{
if (DimHelperSub<i,j,TFunctor>::Execute(obj, data))
{
return true;
}
return DimHelper<i-1,j,TFunctor>::Execute(obj, data);
}
};
/** Specialized template version of DimHelper that indicates the end
* of the dimension combination matrix, thus does nothing.
*/
template< unsigned int j, template < unsigned int, unsigned int> class TFunctor>
class DimHelper<1,j, TFunctor >
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper*, const map::core::String&)
{
//just unwind. We are done.
return false;
}
};
/** Functor that checks of the dimension of the registration is supported and can
* be written.
*/
template<unsigned int i, unsigned int j>
class CanWrite
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& = "")
{
bool result = false;
result = dynamic_cast<const map::core::Registration<i,j> *>(obj->GetRegistration()) != nullptr;
return result;
}
};
/** Functor that writes the registration to a file if it has the right dimensionality.
*/
template<unsigned int i, unsigned int j>
class WriteReg
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data)
{
const map::core::Registration<i,j>* pReg = dynamic_cast<const map::core::Registration<i,j>*>(obj->GetRegistration());
if (pReg == nullptr)
{
return false;
}
typedef map::io::RegistrationFileWriter<i,j> WriterType;
typename WriterType::Pointer writer = WriterType::New();
writer->setExpandLazyKernels(false);
try
{
writer->write(pReg,data);
}
catch (const itk::ExceptionObject& e)
{
std::cout << e.what() << std::endl;
throw;
}
return true;
}
};
MAPRegistrationWrapperIO::MAPRegistrationWrapperIO(const MAPRegistrationWrapperIO& other)
: AbstractFileIO(other)
{
}
MAPRegistrationWrapperIO::MAPRegistrationWrapperIO() : AbstractFileIO(mitk::MAPRegistrationWrapper::GetStaticNameOfClass())
{
std::string category = "MatchPoint Registration File";
CustomMimeType customMimeType;
customMimeType.SetCategory(category);
customMimeType.AddExtension("mapr");
this->AbstractFileIOWriter::SetMimeType(customMimeType);
this->AbstractFileIOWriter::SetDescription(category);
customMimeType.AddExtension("mapr.xml");
customMimeType.AddExtension("MAPR");
customMimeType.AddExtension("MAPR.XML");
this->AbstractFileIOReader::SetMimeType(customMimeType);
this->AbstractFileIOReader::SetDescription(category);
this->RegisterService();
}
void MAPRegistrationWrapperIO::Write()
{
bool success = false;
const BaseData* input = this->GetInput();
if (input == nullptr)
{
mitkThrow() << "Cannot write data. Data pointer is nullptr.";
}
const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>(input);
if (wrapper == nullptr)
{
mitkThrow() << "Cannot write data. Data pointer is not a Registration wrapper.";
}
std::ostream* writeStream = this->GetOutputStream();
std::string fileName = this->GetOutputLocation();
if (writeStream)
{
fileName = this->GetLocalFileName();
}
// Switch the current locale to "C"
LocaleSwitch localeSwitch("C");
try
{
success = DimHelper<3,3,WriteReg>::Execute(wrapper, fileName);
}
catch (const std::exception& e)
{
mitkThrow() << e.what();
}
if (!success)
{
mitkThrow() << "Cannot write registration. Currently only registrations up to 4D are supported.";
}
}
AbstractFileIO::ConfidenceLevel MAPRegistrationWrapperIO::GetWriterConfidenceLevel() const
{
const mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>(this->GetInput());
if (regWrapper == nullptr)
{
return IFileWriter::Unsupported;
}
// Check if the registration dimension is supported
if (! DimHelper<3,3,CanWrite>::Execute(regWrapper))
{
return IFileWriter::Unsupported;
};
return IFileWriter::Supported;
}
std::vector<BaseData::Pointer > MAPRegistrationWrapperIO::DoRead()
{
std::vector<BaseData::Pointer > result;
LocaleSwitch("C");
std::string fileName = this->GetLocalFileName();
if ( fileName.empty() )
{
mitkThrow() << "Cannot read file. Filename has not been set!";
}
/* Remove the following kernel loader provider because in MITK no lazy file loading should be used
due to conflicts with session loading (end there usage of temporary directories)*/
map::io::RegistrationFileReader::LoaderStackType::unregisterProvider(map::io::LazyFileFieldKernelLoader<2,2>::getStaticProviderName());
map::io::RegistrationFileReader::LoaderStackType::unregisterProvider(map::io::LazyFileFieldKernelLoader<3,3>::getStaticProviderName());
map::io::RegistrationFileReader::Pointer spReader = map::io::RegistrationFileReader::New();
spReader->setPreferLazyLoading(true);
map::core::RegistrationBase::Pointer spReg = spReader->read(fileName);
auto spRegWrapper = mitk::MAPRegistrationWrapper::New(spReg);
result.push_back(spRegWrapper.GetPointer());
return result;
}
AbstractFileIO::ConfidenceLevel MAPRegistrationWrapperIO::GetReaderConfidenceLevel() const
{
AbstractFileIO::ConfidenceLevel result = IFileReader::Unsupported;
std::string fileName = this->GetLocalFileName();
std::ifstream in( fileName.c_str() );
if ( in.good() )
{
result = IFileReader::Supported;
}
in.close();
return result;
}
MAPRegistrationWrapperIO* MAPRegistrationWrapperIO::IOClone() const
{
return new MAPRegistrationWrapperIO(*this);
}
}
diff --git a/Modules/MatchPointRegistration/cmdapps/MapImage.cpp b/Modules/MatchPointRegistration/cmdapps/MapImage.cpp
index 3b57cb5023..f8b1c9d56b 100644
--- a/Modules/MatchPointRegistration/cmdapps/MapImage.cpp
+++ b/Modules/MatchPointRegistration/cmdapps/MapImage.cpp
@@ -1,286 +1,286 @@
/*============================================================================
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.
============================================================================*/
// std includes
#include <string>
#include <numeric>
// itk includes
//#include <itksys/SystemTools.hxx>
// CTK includes
#include <mitkCommandLineParser.h>
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkImageStitchingHelper.h>
struct Settings
{
std::string inFileName = "";
std::string regFileName = "";
std::string outFileName = "";
std::string refGeometryFileName = "";
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear;
double paddingValue = 0;
std::vector<unsigned int> superSamplingFactors;
};
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Registration Tools");
parser.setTitle("Map Image");
parser.setDescription("MiniApp that allows to map a image into a given output geometry by using a given registration.");
parser.setContributor("MIC, German Cancer Research Center (DKFZ)");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"input", "i", mitkCommandLineParser::File, "Input image", "Path to the input images that should be mapped", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
- "Path to the maped image.",
+ "Path to the mapped image.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"registration", "r", mitkCommandLineParser::File, "Registration filee", "Path to the registration that should be used. If no registration is specified, an identity transform is assumed.", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("template",
"t",
mitkCommandLineParser::File,
"Output template image.",
"File path to an image that serves as template for the output geometry. If no template is specified, the geometry of the input image will be used.",
us::Any(),
false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Paths to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("interpolator", "n", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true);
parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true);
parser.addArgument("super-sampling", "s", mitkCommandLineParser::StringList, "Super sampling factor", "Value used for super sampling of the result. E.g. factor 2 will lead to a doubled resolution compared to the used template. If not specified, no super sampling will be done.", us::Any(), true);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs, Settings& settings)
{
try
{
if (parsedArgs.size() == 0)
return false;
settings.inFileName = us::any_cast<std::string>(parsedArgs["input"]);
settings.outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("template"))
{
settings.refGeometryFileName = us::any_cast<std::string>(parsedArgs["template"]);
}
if (parsedArgs.count("registration"))
{
settings.regFileName = us::any_cast<std::string>(parsedArgs["registration"]);
}
if (parsedArgs.count("interpolator"))
{
auto interpolator = us::any_cast<int>(parsedArgs["interpolator"]);
settings.interpolatorType = static_cast<mitk::ImageMappingInterpolator::Type>(interpolator);
}
if (parsedArgs.count("padding"))
{
settings.paddingValue = us::any_cast<float>(parsedArgs["padding"]);
}
settings.superSamplingFactors.clear();
if (parsedArgs.count("super-sampling"))
{
try
{
auto samplingStrings = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["super-sampling"]);
if (samplingStrings.size() != 1 && samplingStrings.size() != 3)
{
std::cerr << "Error. Invalid number of super sampling parameters provided. Either give one (for isometric super sampling) or 3.";
return false;
}
for (const auto& samplingstr : samplingStrings)
{
settings.superSamplingFactors.push_back(std::stoul(samplingstr));
}
if (settings.superSamplingFactors.size() == 1)
{
settings.superSamplingFactors.push_back(settings.superSamplingFactors[0]);
settings.superSamplingFactors.push_back(settings.superSamplingFactors[0]);
}
}
catch (...)
{
std::cerr << "Error. Invalid super sampling parameter provided.";
throw;
}
}
}
catch (...)
{
return false;
}
return true;
}
int main(int argc, char* argv[])
{
mitk::Image::ConstPointer inputImage;
mitk::MAPRegistrationWrapper::ConstPointer registration;
mitk::BaseGeometry::Pointer refGeometry;
Settings settings;
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs, settings))
{
MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information.";
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
std::cout << std::endl << "*******************************************" << std::endl;
std::cout << "Input file: " << settings.inFileName << std::endl;
std::cout << "Output file: " << settings.outFileName << std::endl;
std::cout << "Registration: ";
if (settings.regFileName.empty())
std::cout << "None (Identity)" << std::endl;
else
std::cout << settings.regFileName << std::endl;
std::cout << "Template: ";
if (settings.refGeometryFileName.empty())
std::cout << "None (is input geometry)" << std::endl;
else
std::cout << settings.refGeometryFileName << std::endl;
std::cout << "Padding value: " << settings.paddingValue << std::endl;
std::cout << "Interpolation type: " << settings.interpolatorType << std::endl;
//check for super/sub sampling
if (!settings.superSamplingFactors.empty() && (settings.superSamplingFactors.size() != 1 || settings.superSamplingFactors[0] != 1))
{
std::cout << "Super sampling:";
for (auto value : settings.superSamplingFactors)
{
std::cout << " " << value;
}
std::cout << std::endl;
}
//! [do processing]
try
{
std::cout << "Load input data..." << std::endl;
inputImage = mitk::IOUtil::Load<mitk::Image>(settings.inFileName);
if (inputImage.IsNull())
{
MITK_ERROR << "Cannot load input image.";
return EXIT_FAILURE;
}
std::cout << "Load registration..." << std::endl;
if (settings.regFileName.empty())
{
std::cout << " associated registration: identity" << std::endl;
registration = mitk::GenerateIdentityRegistration3D().GetPointer();
}
else
{
registration = mitk::IOUtil::Load<mitk::MAPRegistrationWrapper>(settings.regFileName);
}
if (registration.IsNull())
{
MITK_ERROR << "Cannot load registration.";
return EXIT_FAILURE;
}
if (settings.refGeometryFileName != "")
{
std::cout << "Load reference image..." << std::endl;
auto refImage = mitk::IOUtil::Load<mitk::Image>(settings.refGeometryFileName, &readerFilterFunctor);
if (refImage.IsNotNull())
{
refGeometry = refImage->GetGeometry();
}
else
{
MITK_ERROR << "Cannot load reference geometry image.";
return EXIT_FAILURE;
}
}
else
{
refGeometry = inputImage->GetGeometry();
}
//check for super/sub sampling
if (!settings.superSamplingFactors.empty() && (settings.superSamplingFactors.size()!=1 || settings.superSamplingFactors[0]!=1))
{
refGeometry = mitk::ImageMappingHelper::GenerateSuperSampledGeometry(refGeometry,
settings.superSamplingFactors[0],
settings.superSamplingFactors[1],
settings.superSamplingFactors[2]);
}
std::cout << "Map the images ..." << std::endl;
auto output = mitk::ImageMappingHelper::map(inputImage, registration, false, settings.paddingValue, refGeometry, true, 0, settings.interpolatorType);
std::cout << "Save output image: " << settings.outFileName << std::endl;
mitk::IOUtil::Save(output, settings.outFileName);
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp b/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp
index 17e05af7f5..7e32fa9b6a 100644
--- a/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp
+++ b/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp
@@ -1,482 +1,482 @@
/*============================================================================
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 "mitkCommandLineParser.h"
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkPointSet.h>
#include <mitkImageTimeSelector.h>
#include <itkStdStreamLogOutput.h>
// MatchPoint
#include <mapRegistrationAlgorithmInterface.h>
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapPointSetRegistrationAlgorithmInterface.h>
#include <mapMaskedRegistrationAlgorithmInterface.h>
#include <mapMetaPropertyAlgorithmInterface.h>
#include <mapMetaProperty.h>
#include <mapConvert.h>
#include <mapDeploymentDLLAccess.h>
#include <mapDeploymentDLLHandle.h>
#include <mapRegistrationBase.h>
#include <nlohmann/json.hpp>
struct Settings
{
std::string movingFileName = "";
std::string targetFileName = "";
std::string outFileName = "";
std::string algFileName = "";
std::string parameters = "";
};
void SetupParser(mitkCommandLineParser& parser)
{
parser.setTitle("Match Image");
parser.setCategory("Registration Tools");
parser.setDescription("");
parser.setContributor("MIC, German Cancer Research Center (DKFZ)");
parser.setArgumentPrefix("--", "-");
// Add command line argument names
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"moving", "m",
mitkCommandLineParser::File,
- "Moving image files", "Path to the data that should be registred into the target space.",
+ "Moving image files", "Path to the data that should be registered into the target space.",
us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"target", "t",
mitkCommandLineParser::File,
"Tareget image files", "Path to the data that should be the target data on which the moving data should be registered.",
us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"algorithm", "a",
mitkCommandLineParser::File,
"Registration algorithm", "Path to the registration algorithm that should be used for registration.",
us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
"Path to the generated registration.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"parameters", "p", mitkCommandLineParser::String, "Parameters", "Json string containing a json object that contains the parameters that should be passed to the algorithm as key value pairs.");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
}
bool ConfigureApplicationSettings(std::map<std::string, us::Any> parsedArgs, Settings& settings)
{
try
{
if (parsedArgs.size() == 0)
return false;
settings.movingFileName = us::any_cast<std::string>(parsedArgs["moving"]);
settings.targetFileName = us::any_cast<std::string>(parsedArgs["target"]);
settings.outFileName = us::any_cast<std::string>(parsedArgs["output"]);
settings.algFileName = us::any_cast<std::string>(parsedArgs["algorithm"]);
if (parsedArgs.count("parameters") > 0)
{
settings.parameters = us::any_cast<std::string>(parsedArgs["parameters"]);
}
}
catch (...)
{
return false;
}
return true;
}
map::deployment::RegistrationAlgorithmBasePointer loadAlgorithm(const Settings& settings)
{
map::deployment::RegistrationAlgorithmBasePointer spAlgorithmBase = nullptr;
std::cout << std::endl << "Load registration algorithm..." << std::endl;
map::deployment::DLLHandle::Pointer spHandle = nullptr;
spHandle = map::deployment::openDeploymentDLL(settings.algFileName);
if (spHandle.IsNull())
{
mapDefaultExceptionStaticMacro(<<
"Cannot open deployed registration algorithm file.");
}
- std::cout << "... libary opened..." << std::endl;
+ std::cout << "... library opened..." << std::endl;
std::cout << "Algorithm information: " << std::endl;
spHandle->getAlgorithmUID().Print(std::cout, 2);
std::cout << std::endl;
- //Now load the algorthm from DLL
+ //Now load the algorithm from DLL
spAlgorithmBase = map::deployment::getRegistrationAlgorithm(spHandle);
if (spAlgorithmBase.IsNotNull())
{
std::cout << "... done" << std::endl << std::endl;
}
else
{
mapDefaultExceptionStaticMacro(<< "Cannot create algorithm instance");
}
return spAlgorithmBase;
};
mitk::Image::Pointer ExtractFirstFrame(const mitk::Image* dynamicImage)
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(dynamicImage);
imageTimeSelector->SetTimeNr(0);
imageTimeSelector->UpdateLargestPossibleRegion();
return imageTimeSelector->GetOutput();
}
template <typename TValueType>
map::core::MetaPropertyBase::Pointer
CheckCastAndSetProp(const nlohmann::json& value)
{
map::core::MetaPropertyBase::Pointer prop;
try
{
const auto castedValue = value.get<TValueType>();
prop = map::core::MetaProperty<TValueType>::New(castedValue).GetPointer();
}
catch (const std::exception& e)
{
MITK_ERROR << "Cannot convert value \"" << value << "\" into type: " << typeid(TValueType).name() << ". Details: " << e.what();
}
catch (...)
{
MITK_ERROR << "Unkown error. Cannot convert value \"" << value << "\" into type: " << typeid(TValueType).name();
}
return prop;
};
template <typename TValueType>
map::core::MetaPropertyBase::Pointer
CheckCastAndSetItkArrayProp(const nlohmann::json& valueSequence)
{
using ArrayType = ::itk::Array<TValueType>;
ArrayType castedValue;
map::core::MetaPropertyBase::Pointer prop;
try
{
castedValue.SetSize(valueSequence.size());
typename ::itk::Array<TValueType>::SizeValueType index = 0;
for (const auto& element : valueSequence)
{
const auto castedElement = element.template get<TValueType>();
castedValue[index] = castedElement;
}
prop = map::core::MetaProperty<::itk::Array<TValueType>>::New(castedValue).GetPointer();
}
catch (const std::exception& e)
{
MITK_ERROR << "Cannot convert value \"" << valueSequence << "\" into type: " << typeid(ArrayType).name() << ". Details: " << e.what();
}
catch (...)
{
MITK_ERROR << "Unkown error. Cannot convert value \"" << valueSequence << "\" into type: " << typeid(ArrayType).name();
}
return prop;
};
::map::core::MetaPropertyBase::Pointer
WrapIntoMetaProperty(const ::map::algorithm::MetaPropertyInfo* pInfo, const nlohmann::json& value)
{
map::core::MetaPropertyBase::Pointer metaProp;
if (pInfo == nullptr)
{
return metaProp;
}
if (pInfo->getTypeInfo() == typeid(int)) {
metaProp = CheckCastAndSetProp<int>(value);
}
else if (pInfo->getTypeInfo() == typeid(unsigned int)) {
metaProp = CheckCastAndSetProp<unsigned int>(value);
}
else if (pInfo->getTypeInfo() == typeid(long)) {
metaProp = CheckCastAndSetProp<long>(value);
}
else if (pInfo->getTypeInfo() == typeid(unsigned long)) {
metaProp = CheckCastAndSetProp<unsigned long>(value);
}
else if (pInfo->getTypeInfo() == typeid(float)) {
metaProp = CheckCastAndSetProp<float>(value);
}
else if (pInfo->getTypeInfo() == typeid(double)) {
metaProp = CheckCastAndSetProp<double>(value);
}
else if (pInfo->getTypeInfo() == typeid(::itk::Array<double>)) {
metaProp = CheckCastAndSetItkArrayProp< double >(value);
}
else if (pInfo->getTypeInfo() == typeid(bool)) {
metaProp = CheckCastAndSetProp< bool >(value);
}
else if (pInfo->getTypeInfo() == typeid(::map::core::String))
{
metaProp = map::core::MetaProperty<map::core::String>::New(value).GetPointer();
}
return metaProp;
};
void OnMapAlgorithmEvent(::itk::Object*, const itk::EventObject& event, void*)
{
const map::events::AlgorithmEvent* pAlgEvent = dynamic_cast<const map::events::AlgorithmEvent*>(&event);
const map::events::AlgorithmWrapperEvent* pWrapEvent =
dynamic_cast<const map::events::AlgorithmWrapperEvent*>(&event);
const map::events::InitializingAlgorithmEvent* pInitEvent =
dynamic_cast<const map::events::InitializingAlgorithmEvent*>(&event);
const map::events::StartingAlgorithmEvent* pStartEvent =
dynamic_cast<const map::events::StartingAlgorithmEvent*>(&event);
const map::events::StoppingAlgorithmEvent* pStoppingEvent =
dynamic_cast<const map::events::StoppingAlgorithmEvent*>(&event);
const map::events::StoppedAlgorithmEvent* pStoppedEvent =
dynamic_cast<const map::events::StoppedAlgorithmEvent*>(&event);
const map::events::FinalizingAlgorithmEvent* pFinalizingEvent =
dynamic_cast<const map::events::FinalizingAlgorithmEvent*>(&event);
const map::events::FinalizedAlgorithmEvent* pFinalizedEvent =
dynamic_cast<const map::events::FinalizedAlgorithmEvent*>(&event);
if (pInitEvent)
{
std::cout <<"Initializing algorithm ..." << std::endl;
}
else if (pStartEvent)
{
std::cout <<"Starting algorithm ..." << std::endl;
}
else if (pStoppingEvent)
{
std::cout <<"Stopping algorithm ..." << std::endl;
}
else if (pStoppedEvent)
{
std::cout <<"Stopped algorithm ..." << std::endl;
if (!pStoppedEvent->getComment().empty())
{
std::cout <<"Stopping condition: " << pStoppedEvent->getComment() << std::endl;
}
}
else if (pFinalizingEvent)
{
std::cout <<"Finalizing algorithm and results ..." << std::endl;
}
else if (pFinalizedEvent)
{
std::cout <<"Finalized algorithm ..." << std::endl;
}
else if (pAlgEvent && !pWrapEvent)
{
std::cout << pAlgEvent->getComment() << std::endl;
}
}
int main(int argc, char* argv[])
{
std::cout << "MitkMatchImage - Generic light weight image registration tool based on MatchPoint." << std::endl;
Settings settings;
mitkCommandLineParser parser;
SetupParser(parser);
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!ConfigureApplicationSettings(parsedArgs, settings))
{
MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information.";
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
std::cout << std::endl << "*******************************************" << std::endl;
std::cout << "Moving file: " << settings.movingFileName << std::endl;
std::cout << "Target file: " << settings.targetFileName << std::endl;
std::cout << "Output file: " << settings.outFileName << std::endl;
std::cout << "Algorithm location: " << settings.algFileName << std::endl;
try
{
auto algorithm = loadAlgorithm(settings);
auto command = ::itk::CStyleCommand::New();
command->SetCallback(OnMapAlgorithmEvent);
algorithm->AddObserver(::map::events::AlgorithmEvent(), command);
auto metaPropInterface = dynamic_cast<map::algorithm::facet::MetaPropertyAlgorithmInterface*>(algorithm.GetPointer());
if (!settings.parameters.empty())
{
if (nullptr == metaPropInterface)
{
MITK_WARN << "loaded algorithm does not support custom parameterization. Passed user parameters are ignored.";
}
else
{
nlohmann::json paramMap;
std::string parseError = "";
try
{
paramMap = nlohmann::json::parse(settings.parameters);
}
catch (const std::exception& e)
{
parseError = e.what();
}
if (!parseError.empty())
{
mitkThrow() << "Cannot parametrize algorithm. Passed JSON parameter string seems to be invalid. Passed string: \"" << settings.parameters << "\". Error details: " << parseError;
}
std::cout << "Configuring algorithm with user specified parameters ..." << std::endl;
for (const auto& [key, val] : paramMap.items())
{
const auto info = metaPropInterface->getPropertyInfo(key);
if (info.IsNotNull())
{
if (info->isWritable())
{
std::cout << "Set meta property: " << key << " = " << val << std::endl;
::map::core::MetaPropertyBase::Pointer prop = WrapIntoMetaProperty(info, val);
if (prop.IsNull())
{
mitkThrow() << "Error. Cannot set specified meta property. Type conversion is not supported or value cannot be converted into type. Property name: " << info->getName() << "; property type: " << info->getTypeName();
}
else
{
metaPropInterface->setProperty(key, prop);
}
}
else
{
mitkThrow() << "Cannot parametrize algorithm. A passed parameter is not writable for the algorithm. Violating parameter: \"" << key << "\".";
}
}
else
{
auto knownProps = metaPropInterface->getPropertyInfos();
std::ostringstream knownPropsNameString;
for (const auto& knownProp : knownProps)
{
knownPropsNameString << knownProp->getName() << "; ";
}
mitkThrow() << "Cannot parametrize algorithm. A parameter is unkown to algorithm. Unkown passed parameter: \"" << key << "\". Known parameters: " << knownPropsNameString.str();
}
}
}
}
std::cout << "Load moving data..." << std::endl;
auto movingImage = mitk::IOUtil::Load<mitk::Image>(settings.movingFileName);
if (movingImage.IsNull())
{
MITK_ERROR << "Cannot load moving image.";
return EXIT_FAILURE;
}
if (movingImage->GetTimeSteps() > 1)
{
movingImage = mitk::SelectImageByTimeStep(movingImage, 0)->Clone(); //we have to clone because SelectImageByTimeStep
//only generates as new view of the data and we
//are overwriting the only smartpointer to the source.
- std::cout << "Moving image has multiple time steps. Use first time step for registartion." << std::endl;
+ std::cout << "Moving image has multiple time steps. Use first time step for registration." << std::endl;
}
std::cout << "Load target data..." << std::endl;
auto targetImage = mitk::IOUtil::Load<mitk::Image>(settings.targetFileName);
if (targetImage.IsNull())
{
MITK_ERROR << "Cannot load target image.";
return EXIT_FAILURE;
}
if (targetImage->GetTimeSteps() > 1)
{
targetImage = mitk::SelectImageByTimeStep(targetImage, 0)->Clone(); //we have to clone because SelectImageByTimeStep
//only generates as new view of the data and we
//are overwriting the only smartpointer to the source.
- std::cout << "Target image has multiple time steps. Use first time step for registartion." << std::endl;
+ std::cout << "Target image has multiple time steps. Use first time step for registration." << std::endl;
}
std::cout << "Start registration...." << std::endl;
mitk::MAPAlgorithmHelper helper(algorithm);
helper.SetData(movingImage, targetImage);
::itk::StdStreamLogOutput::Pointer spStreamLogOutput = ::itk::StdStreamLogOutput::New();
spStreamLogOutput->SetStream(std::cout);
map::core::Logbook::addAdditionalLogOutput(spStreamLogOutput);
auto registration = helper.GetRegistration();
// wrap the registration in a data node
if (registration.IsNull())
{
MITK_ERROR << "No valid registration generated";
return EXIT_FAILURE;
}
auto regWrapper = mitk::MAPRegistrationWrapper::New(registration);
std::cout << "Store registration...." << std::endl;
mitk::IOUtil::Save(regWrapper, settings.outFileName);
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
diff --git a/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp b/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp
index 159d96a34f..e486a7a744 100644
--- a/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp
+++ b/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp
@@ -1,226 +1,226 @@
/*============================================================================
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.
============================================================================*/
// std includes
#include <string>
#include <numeric>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkImageStitchingHelper.h>
mitkCommandLineParser::StringContainerType inFilenames;
mitkCommandLineParser::StringContainerType regFilenames;
std::string outFileName;
std::string refGeometryFileName;
std::vector<mitk::Image::ConstPointer> images;
std::vector<mitk::MAPRegistrationWrapper::ConstPointer> registrations;
mitk::BaseGeometry::Pointer refGeometry;
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear;
double paddingValue = 0;
itk::StitchStrategy stitchStratgy = itk::StitchStrategy::Mean;
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Registration Tools");
parser.setTitle("Stitch 3D Images");
parser.setDescription("MiniApp that allows to map and stitch 3D images into a given output geometry.");
parser.setContributor("MIC, German Cancer Research Center (DKFZ)");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
- "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be mapped and stitched", us::Any(), false, false, false, mitkCommandLineParser::Input);
+ "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Paths to the input images that should be mapped and stitched", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
"Path to the fused 3D+t image.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument("template",
"t",
mitkCommandLineParser::File,
"Output template image.",
"File path to an image that serves as template for the output geometry.",
us::Any(),
false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Paths to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("interpolator", "n", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true);
parser.addArgument("strategy", "s", mitkCommandLineParser::Int, "Stitch strategy", "Strategy used for stitching the images. 0: Mean -> computes the mean value of all input images that cover an output pixel (default strategy). 1: BorderDistance -> Uses the input pixel that has the largest minimal distance to its image borders", us::Any(2), true);
parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
try
{
if (parsedArgs.size() == 0)
return false;
inFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["inputs"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("template"))
{
refGeometryFileName = us::any_cast<std::string>(parsedArgs["template"]);
}
if (parsedArgs.count("registrations"))
{
regFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["registrations"]);
}
else
{
regFilenames.resize(inFilenames.size());
std::fill(regFilenames.begin(), regFilenames.end(), "");
}
if (parsedArgs.count("interpolator"))
{
auto interpolator = us::any_cast<int>(parsedArgs["interpolator"]);
interpolatorType = static_cast<mitk::ImageMappingInterpolator::Type>(interpolator);
}
if (parsedArgs.count("padding"))
{
paddingValue = us::any_cast<float>(parsedArgs["padding"]);
}
if (parsedArgs.count("strategy"))
{
auto temp = us::any_cast<int>(parsedArgs["strategy"]);
stitchStratgy = static_cast<itk::StitchStrategy>(temp);
}
}
catch (...)
{
return false;
}
return true;
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs))
{
MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information.";
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
if(regFilenames.size() != inFilenames.size())
{
MITK_ERROR << "Cannot stitch inputs. The number of specified registrations does not match the number of inputs.";
return EXIT_FAILURE;
}
//! [do processing]
try
{
std::cout << "Load images:" << std::endl;
unsigned int index = 0;
for (auto path : inFilenames)
{
std::cout << "#"<<index<<" " << path << std::endl;
auto image = mitk::IOUtil::Load<mitk::Image>(path, &readerFilterFunctor);
images.push_back(image.GetPointer());
if (regFilenames[index].empty())
{
std::cout << " associated registration: identity" << std::endl;
registrations.push_back(mitk::GenerateIdentityRegistration3D().GetPointer());
}
else
{
std::cout << " associated registration: " << regFilenames[index] << std::endl;
auto reg = mitk::IOUtil::Load<mitk::MAPRegistrationWrapper>(regFilenames[index]);
registrations.push_back(reg.GetPointer());
}
++index;
}
std::cout << "Reference image: " << refGeometryFileName << std::endl << std::endl;
auto refImage = mitk::IOUtil::Load<mitk::Image>(refGeometryFileName, &readerFilterFunctor);
if (refImage.IsNotNull())
{
refGeometry = refImage->GetGeometry();
}
std::cout << "Padding value: " << paddingValue << std::endl;
std::cout << "Stitch strategy: ";
if (itk::StitchStrategy::Mean == stitchStratgy)
{
std::cout << "Mean " << std::endl;
}
else
{
std::cout << "BorderDistance" << std::endl;
}
std::cout << "Stitch the images ..." << std::endl;
auto output = mitk::StitchImages(images, registrations, refGeometry,paddingValue,stitchStratgy,interpolatorType);
std::cout << "Save output image: " << outFileName << std::endl;
mitk::IOUtil::Save(output, outFileName);
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
index c72836aeca..05e5b6e145 100644
--- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
+++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
@@ -1,330 +1,330 @@
/*============================================================================
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 itkStitchImageFilter_h
#define itkStitchImageFilter_h
#include "itkFixedArray.h"
#include "itkTransform.h"
#include "itkImageRegionIterator.h"
#include "itkImageToImageFilter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkSize.h"
#include "itkDefaultConvertPixelTraits.h"
#include "itkDataObjectDecorator.h"
namespace itk
{
enum class StitchStrategy
{
Mean = 0, //use the mean value of all inputs that can provide a pixel vaule
- BorderDistance = 1 //use the value that is largest minimal distance to its image borders (use e.g. if vaules tend to be not reliable at borders)
+ BorderDistance = 1 //use the value that is largest minimal distance to its image borders (use e.g. if values tend to be not reliable at borders)
};
std::ostream& operator<< (std::ostream& os, const itk::StitchStrategy& strategy)
{
if (itk::StitchStrategy::Mean == strategy)
os << "Mean";
else if (itk::StitchStrategy::BorderDistance == strategy)
os << "BorderDistance";
else
- os << "unkown";
+ os << "unknown";
return os;
};
/** \class StitchImageFilter
* \brief ITK filter that resamples/stitches multiple images into a given reference geometry.
*
* StitchImageFilter is similar to itk's ResampleImageFilter, but in difference to the last
* mentioned StitchImageFilter is able to resample multiple input images at once (with a transform
* for each input image). If multiple input images cover the output region the behavior depends on
* the StitchStragy:
* - Mean: a weighted sum of all voxels mapped input pixel values will be calculated.
- * - BorderDistance: the voxels will be choosen that have the largest minimal distance to its own image borders.
+ * - BorderDistance: the voxels will be chosen that have the largest minimal distance to its own image borders.
*
* All other behaviors are similar to itk::ResampleImageFilter. See the filter's description for
* more details.
*/
template< typename TInputImage,
typename TOutputImage,
typename TInterpolatorPrecisionType = double,
typename TTransformPrecisionType = TInterpolatorPrecisionType>
class StitchImageFilter :
public ImageToImageFilter< TInputImage, TOutputImage >
{
public:
/** Standard class typedefs. */
typedef StitchImageFilter Self;
typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
typedef TInputImage InputImageType;
typedef TOutputImage OutputImageType;
typedef typename InputImageType::Pointer InputImagePointer;
typedef typename InputImageType::ConstPointer InputImageConstPointer;
typedef typename OutputImageType::Pointer OutputImagePointer;
typedef typename InputImageType::RegionType InputImageRegionType;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(StitchImageFilter, ImageToImageFilter);
/** Number of dimensions. */
itkStaticConstMacro(ImageDimension, unsigned int,
TOutputImage::ImageDimension);
itkStaticConstMacro(InputImageDimension, unsigned int,
TInputImage::ImageDimension);
/** base type for images of the current ImageDimension */
typedef ImageBase< itkGetStaticConstMacro(ImageDimension) > ImageBaseType;
/**
* Transform typedef.
*/
typedef Transform< TTransformPrecisionType,
itkGetStaticConstMacro(ImageDimension),
itkGetStaticConstMacro(ImageDimension) > TransformType;
typedef typename TransformType::ConstPointer TransformPointerType;
typedef DataObjectDecorator<TransformType> DecoratedTransformType;
typedef typename DecoratedTransformType::Pointer DecoratedTransformPointer;
/** Interpolator typedef. */
typedef InterpolateImageFunction< InputImageType,
TInterpolatorPrecisionType > InterpolatorType;
typedef typename InterpolatorType::Pointer InterpolatorPointerType;
typedef typename InterpolatorType::OutputType InterpolatorOutputType;
typedef DefaultConvertPixelTraits< InterpolatorOutputType > InterpolatorConvertType;
typedef typename InterpolatorConvertType::ComponentType ComponentType;
typedef LinearInterpolateImageFunction< InputImageType,
TInterpolatorPrecisionType > LinearInterpolatorType;
typedef typename LinearInterpolatorType::Pointer
LinearInterpolatorPointerType;
/** Image size typedef. */
typedef Size< itkGetStaticConstMacro(ImageDimension) > SizeType;
/** Image index typedef. */
typedef typename TOutputImage::IndexType IndexType;
/** Image point typedef. */
typedef typename InterpolatorType::PointType PointType;
//typedef typename TOutputImage::PointType PointType;
/** Image pixel value typedef. */
typedef typename TOutputImage::PixelType PixelType;
typedef typename TInputImage::PixelType InputPixelType;
typedef DefaultConvertPixelTraits<PixelType> PixelConvertType;
typedef typename PixelConvertType::ComponentType PixelComponentType;
/** Input pixel continuous index typdef */
typedef ContinuousIndex< TTransformPrecisionType, ImageDimension >
ContinuousInputIndexType;
/** Typedef to describe the output image region type. */
typedef typename TOutputImage::RegionType OutputImageRegionType;
/** Image spacing,origin and direction typedef */
typedef typename TOutputImage::SpacingType SpacingType;
typedef typename TOutputImage::PointType OriginPointType;
typedef typename TOutputImage::DirectionType DirectionType;
using Superclass::GetInput;
/** Typedef the reference image type to be the ImageBase of the OutputImageType */
typedef ImageBase<ImageDimension> ReferenceImageBaseType;
using Superclass::SetInput;
void SetInput(const InputImageType* image) override;
void SetInput(unsigned int index, const InputImageType* image) override;
- /** Convinience methods that allows setting of input image and its transform in
+ /** Convenience methods that allows setting of input image and its transform in
one call.*/
virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform);
virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator);
const TransformType* GetTransform(unsigned int index) const;
const InterpolatorType* GetInterpolator(unsigned int index) const;
/** Get/Set the size of the output image. */
itkSetMacro(Size, SizeType);
itkGetConstReferenceMacro(Size, SizeType);
/** Get/Set the pixel value when a transformed pixel is outside of the
* image. The default default pixel value is 0. */
itkSetMacro(DefaultPixelValue, PixelType);
itkGetConstReferenceMacro(DefaultPixelValue, PixelType);
/** Set the output image spacing. */
itkSetMacro(OutputSpacing, SpacingType);
virtual void SetOutputSpacing(const double *values);
/** Get the output image spacing. */
itkGetConstReferenceMacro(OutputSpacing, SpacingType);
/** Set the output image origin. */
itkSetMacro(OutputOrigin, OriginPointType);
virtual void SetOutputOrigin(const double *values);
/** Get the output image origin. */
itkGetConstReferenceMacro(OutputOrigin, OriginPointType);
- /** Set the output direciton cosine matrix. */
+ /** Set the output direction cosine matrix. */
itkSetMacro(OutputDirection, DirectionType);
itkGetConstReferenceMacro(OutputDirection, DirectionType);
/** Helper method to set the output parameters based on this image. */
void SetOutputParametersFromImage(const ImageBaseType *image);
/** Set the start index of the output largest possible region.
* The default is an index of all zeros. */
itkSetMacro(OutputStartIndex, IndexType);
/** Get the start index of the output largest possible region. */
itkGetConstReferenceMacro(OutputStartIndex, IndexType);
/** Set a reference image to use to define the output information.
- * By default, output information is specificed through the
+ * By default, output information is specified through the
* SetOutputSpacing, Origin, and Direction methods. Alternatively,
* this method can be used to specify an image from which to
* copy the information. UseReferenceImageOn must be set to utilize the
* reference image. */
itkSetInputMacro(ReferenceImage, ReferenceImageBaseType);
/** Get the reference image that is defining the output information. */
itkGetInputMacro(ReferenceImage, ReferenceImageBaseType);
/** Turn on/off whether a specified reference image should be used to define
* the output information. */
itkSetMacro(UseReferenceImage, bool);
itkBooleanMacro(UseReferenceImage);
itkGetConstMacro(UseReferenceImage, bool);
itkSetMacro(StitchStrategy, StitchStrategy);
itkGetConstMacro(StitchStrategy, StitchStrategy);
/** StitchImageFilter produces an image which is a different size
* than its input. As such, it needs to provide an implementation
* for GenerateOutputInformation() in order to inform the pipeline
* execution model. The original documentation of this method is
* below. \sa ProcessObject::GenerateOutputInformaton() */
virtual void GenerateOutputInformation() ITK_OVERRIDE;
/** StitchImageFilter needs a different input requested region than
* the output requested region. As such, StitchImageFilter needs
* to provide an implementation for GenerateInputRequestedRegion()
* in order to inform the pipeline execution model.
* \sa ProcessObject::GenerateInputRequestedRegion() */
virtual void GenerateInputRequestedRegion() ITK_OVERRIDE;
/** Set up state of filter before multi-threading.
* InterpolatorType::SetInputImage is not thread-safe and hence
* has to be set up before ThreadedGenerateData */
virtual void BeforeThreadedGenerateData() ITK_OVERRIDE;
/** Set the state of the filter after multi-threading. */
virtual void AfterThreadedGenerateData() ITK_OVERRIDE;
/** Compute the Modified Time based on the changed components. */
ModifiedTimeType GetMTime(void) const ITK_OVERRIDE;
#ifdef ITK_USE_CONCEPT_CHECKING
// Begin concept checking
itkConceptMacro( OutputHasNumericTraitsCheck,
( Concept::HasNumericTraits< PixelComponentType > ) );
// End concept checking
#endif
protected:
StitchImageFilter();
~StitchImageFilter() ITK_OVERRIDE {}
void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;
/** Override VeriyInputInformation() since this filter's inputs do
* not need to occoupy the same physical space.
*
* \sa ProcessObject::VerifyInputInformation
*/
virtual void VerifyInputInformation() const ITK_OVERRIDE { }
/** StitchImageFilter can be implemented as a multithreaded filter.
* Therefore, this implementation provides a ThreadedGenerateData()
* routine which is called for each processing thread. The output
* image data is allocated automatically by the superclass prior
* to calling ThreadedGenerateData().
* ThreadedGenerateData can only write to the portion of the output image
* specified by the parameter "outputRegionForThread"
* \sa ImageToImageFilter::ThreadedGenerateData(),
* ImageToImageFilter::GenerateData() */
virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
ThreadIdType threadId) ITK_OVERRIDE;
/** Cast pixel from interpolator output to PixelType. */
virtual PixelType CastPixelWithBoundsChecking( const InterpolatorOutputType value,
const ComponentType minComponent,
const ComponentType maxComponent) const;
void SetTransform(unsigned int index, const TransformType* transform);
/** Helper that ensures that a transform is specified for every input image.
If a input image has no specified transforms, an identity transform will
be created and set as default.*/
void EnsureTransforms();
/** Helper that ensures that an interpolator is specified for every input image.
If a input image has no specified interpolator, a linear interpolator will
be created and set as default.*/
void EnsureInterpolators();
static std::string GetTransformInputName(unsigned int index);
private:
ITK_DISALLOW_COPY_AND_ASSIGN(StitchImageFilter);
typedef std::vector<const InputImageType*> InputImageVectorType;
typedef std::map<const InputImageType*, typename TransformType::ConstPointer> TransformMapType;
typedef std::map<const InputImageType*, InterpolatorPointerType> InterpolatorMapType;
InputImageVectorType GetInputs();
TransformMapType GetTransforms();
InterpolatorMapType m_Interpolators; // Image function for
// interpolation
PixelType m_DefaultPixelValue; // default pixel value
// if the point is
// outside the image
SizeType m_Size; // Size of the output image
SpacingType m_OutputSpacing; // output image spacing
OriginPointType m_OutputOrigin; // output image origin
DirectionType m_OutputDirection; // output image direction cosines
IndexType m_OutputStartIndex; // output image start index
bool m_UseReferenceImage;
StitchStrategy m_StitchStrategy;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkStitchImageFilter.tpp"
#endif
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h b/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h
index ff4e573d68..1f1a3b6e17 100644
--- a/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h
@@ -1,67 +1,67 @@
/*============================================================================
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 mitkImageStitchingHelper_h
#define mitkImageStitchingHelper_h
#include "mapRegistrationBase.h"
#include "mitkImage.h"
#include "mitkGeometry3D.h"
#include "mitkMAPRegistrationWrapper.h"
#include "mitkImageMappingHelper.h"
#include <itkStitchImageFilter.h>
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/**Helper that stitches a given vector of input images
* @param inputs vector of input images that should be stitched.
* @param registrations vector of registrations that should be used for mapping of the inputs before stitching.
* the method assumes that order of registrations is the same as the order of inputs, thus for the n-th input
* the n-th registration will be used.
* @param resultGeometry Pointer to the Geometry object that specifies the grid of the result image.
* @param paddingValue Indicates the value that should be used if an out of input error occurs (and throwOnOutOfInputAreaError is false).
* @param interpolatorType Indicates the type of interpolation strategy that should be used.
* @param stitchStrategy Strategy used if more than one input can contribute. for more details see the documentation of itk::StitchStrategy.
* @pre inputs must not be empty and contain valid instances
* @pre registration must have same size as inputs and contain valid instances.
* @pre Dimensionality of the registrations must match with the inputs
* @pre resultGeometry must be valid.
* @remark The helper currently only supports 3D images.
* @result Pointer to the resulting mapped image.h*/
MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector<Image::ConstPointer> inputs,
std::vector<::map::core::RegistrationBase::ConstPointer> registrations,
const BaseGeometry* resultGeometry,
const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean,
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear);
MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector<Image::ConstPointer> inputs,
std::vector<MAPRegistrationWrapper::ConstPointer> registrations,
const BaseGeometry* resultGeometry,
const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean,
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear);
/**@overload
- * Convinience version that uses identity transforms form the registrations.
+ * Convenience version that uses identity transforms form the registrations.
*/
MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector<Image::ConstPointer> inputs,
const BaseGeometry* resultGeometry,
const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean,
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear);
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h b/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h
index b3460d25c7..19d56bd003 100644
--- a/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h
+++ b/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h
@@ -1,273 +1,273 @@
/*============================================================================
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 mitkMAPRegistrationWrapper_h
#define mitkMAPRegistrationWrapper_h
//MITK
#include <mitkBaseData.h>
#include <mitkGeometry3D.h>
//MatchPoint
#include <mapRegistrationBase.h>
#include <mapRegistration.h>
#include <mapExceptionObjectMacros.h>
#include <mapContinuousElements.h>
//MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/*!
\brief MAPRegistrationWrapper
Wrapper class to allow the handling of MatchPoint registration objects as mitk data (e.g. in the data explorer).
*/
class MITKMATCHPOINTREGISTRATION_EXPORT MAPRegistrationWrapper: public mitk::BaseData
{
public:
mitkClassMacro( MAPRegistrationWrapper, BaseData );
mitkNewMacro1Param( Self, ::map::core::RegistrationBase*);
Identifiable::UIDType GetUID() const override;
bool IsEmptyTimeStep(unsigned int t) const override;
bool IsEmpty() const override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
void SetRequestedRegionToLargestPossibleRegion() override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
bool VerifyRequestedRegion() override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
void SetRequestedRegion(const itk::DataObject*) override;
/*! @brief Gets the number of moving dimensions
@pre valid registration instance must be set.
*/
virtual unsigned int GetMovingDimensions() const;
/*! @brief Gets the number of target dimensions
@pre valid registration instance must be set.
*/
virtual unsigned int GetTargetDimensions() const;
/*! typedefs used for the TagMap
*/
typedef ::map::core::RegistrationBase::TagType TagType;
typedef ::map::core::RegistrationBase::ValueType ValueType;
typedef ::map::core::RegistrationBase::TagMapType TagMapType;
/*! @brief returns the tags associated with this registration
@pre valid registration instance must be set.
@return a TagMapType containing tags
*/
const TagMapType& GetTags() const;
/*! @brief returns the tag value for a specific tag
@pre valid registration instance must be set.
@return the success of the operation
*/
bool GetTagValue(const TagType & tag, ValueType & value) const;
/*! Indicates
@pre valid registration instance must be set.
@return is the target representation limited
@retval true if target representation is limited. Thus it is not guaranteed that all inverse mapping operations
will succeed. Transformation(inverse kernel) covers only a part of the target space).
@retval false if target representation is not limited. Thus it is guaranteed that all inverse mapping operations
will succeed.
*/
bool HasLimitedTargetRepresentation() const;
/*!
@pre valid registration instance must be set.
@return is the moving representation limited
@retval true if moving representation is limited. Thus it is not guaranteed that all direct mapping operations
will succeed. Transformation(direct kernel) covers only a part of the moving space).
@retval false if moving representation is not limited. Thus it is guaranteed that all direct mapping operations
will succeed.
*/
bool HasLimitedMovingRepresentation() const;
/*! Helper function that maps a mitk point (of arbitrary dimension) from moving space to target space.
@remarks The operation might fail, if the moving and target dimension of the registration
is not equal to the dimensionality of the passed points.
@pre valid registration instance must be set.
@param inPoint Reference pointer to a MovingPointType
@param outPoint pointer to a TargetPointType
@return success of operation.
@pre direct mapping kernel must be defined
*/
template <unsigned int VMovingDim, unsigned int VTargetDim>
bool MapPoint(const ::itk::Point<mitk::ScalarType,VMovingDim>& inPoint, ::itk::Point<mitk::ScalarType,VTargetDim>& outPoint) const
{
typedef typename ::map::core::continuous::Elements<VMovingDim>::PointType MAPMovingPointType;
typedef typename ::map::core::continuous::Elements<VTargetDim>::PointType MAPTargetPointType;
if (m_spRegistration.IsNull())
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint);
}
bool result = false;
if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim))
{
MAPMovingPointType tempInP;
MAPTargetPointType tempOutP;
tempInP.CastFrom(inPoint);
typedef ::map::core::Registration<VMovingDim,VTargetDim> CastedRegType;
const CastedRegType* pCastedReg = dynamic_cast<const CastedRegType*>(m_spRegistration.GetPointer());
if (!pCastedReg)
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint);
}
result = pCastedReg->mapPoint(tempInP,tempOutP);
if (result)
{
outPoint.CastFrom(tempOutP);
}
}
return result;
};
/*! Helper function that maps a mitk point (of arbitrary dimension) from target space to moving space
@remarks The operation might faile, if the moving and target dimension of the registration
is not equal to the dimensionalities of the passed points.
@pre valid registration instance must be set.
@param inPoint pointer to a TargetPointType
@param outPoint pointer to a MovingPointType
@return success of operation
*/
template <unsigned int VMovingDim, unsigned int VTargetDim>
bool MapPointInverse(const ::itk::Point<mitk::ScalarType,VTargetDim> & inPoint, ::itk::Point<mitk::ScalarType,VMovingDim> & outPoint) const
{
typedef typename ::map::core::continuous::Elements<VMovingDim>::PointType MAPMovingPointType;
typedef typename ::map::core::continuous::Elements<VTargetDim>::PointType MAPTargetPointType;
if (m_spRegistration.IsNull())
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint);
}
bool result = false;
if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim))
{
MAPTargetPointType tempInP;
MAPMovingPointType tempOutP;
tempInP.CastFrom(inPoint);
typedef ::map::core::Registration<VMovingDim,VTargetDim> CastedRegType;
const CastedRegType* pCastedReg = dynamic_cast<const CastedRegType*>(m_spRegistration.GetPointer());
if (!pCastedReg)
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint);
}
result = pCastedReg->mapPointInverse(tempInP,tempOutP);
if (result)
{
outPoint.CastFrom(tempOutP);
}
}
return result;
};
/*! returns the direct FieldRepresentationDescriptor which defines the part
of the moving space that is guaranteed to be mapped by the direct mapping kernel.
This member converts the internal MatchPoint type into a mitk::Geometry3D.
@pre valid registration instance must be set.
@return smart pointer to a FieldRepresentationDescriptor for the supported registration space in the moving domain.
May be null if the direct registration kernel is global and thus not limited.
- If there is a limitation, the retun value is not nullptr.
+ If there is a limitation, the return value is not nullptr.
@retval nullptr no field representation set/requested by the creating registration algorithm.
*/
mitk::Geometry3D GetDirectFieldRepresentation() const;
/*! returns the inverse FieldRepresentationDescriptor which defines the part
of the target space that is guaranteed to be mapped by the inverse mapping kernel.
This member converts the internal MatchPoint type into a mitk::Geometry3D.
@pre valid registration instance must be set.
@return a const FieldRepresentationDescriptor for the supported registration space in the target domain.
May be null if the inverse registration kernel is global and thus not limited.
- If there is a limitation, the retun value is not nullptr.
+ If there is a limitation, the return value is not nullptr.
@retval nullptr no field representation set/requested by the creating registration algorithm.
*/
mitk::Geometry3D GetInverseFieldRepresentation() const;
/*! forces kernel to precompute, even if it is a LazyFieldKernel
@pre valid registration instance must be set.
@todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten
ist die garantie dieser methode nicht erfllbar. noch berprfen
*/
void PrecomputeDirectMapping();
/*! forces kernel to precompute, even if it is a LazyFieldKernel
@pre valid registration instance must be set.
@todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten
ist die garantie dieser methode nicht erfllbar. noch berprfen
*/
void PrecomputeInverseMapping();
::map::core::RegistrationBase* GetRegistration();
const ::map::core::RegistrationBase* GetRegistration() const;
protected:
void PrintSelf (std::ostream &os, itk::Indent indent) const override;
MAPRegistrationWrapper(::map::core::RegistrationBase* registration);
~MAPRegistrationWrapper() override;
void SetUID(const UIDType& uid) override;
::map::core::RegistrationBase::Pointer m_spRegistration;
private:
MAPRegistrationWrapper& operator = (const MAPRegistrationWrapper&);
MAPRegistrationWrapper(const MAPRegistrationWrapper&);
};
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h b/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h
index e3613516a1..01a44a6b4f 100644
--- a/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h
@@ -1,85 +1,85 @@
/*============================================================================
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 mitkMaskedAlgorithmHelper_h
#define mitkMaskedAlgorithmHelper_h
#include "itkSpatialObject.h"
//MatchPoint
#include "mapRegistrationAlgorithmBase.h"
//MITK
#include <mitkImage.h>
//MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/*!
\brief MaskedAlgorithmHelper
Helper class as an easy bridge to set mitk images as masks for registration algorithms. It is assumed that the
Image indicates the mask by pixel values != 0.
\remark Currently only 2D-2D and 3D-3D algorithms are supported.
\remark Current implementation is not thread-save. Just use one Helper class per registration task.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT MaskedAlgorithmHelper
{
public:
MaskedAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm);
/** Set one or both masks to an algorithm.
* If the algorithm does not support masks it will be ignored.
* @remark Set a mask to nullptr if you don't want to set it.
* @return Indicates if the masks could be set/was supported by algorithm.*/
bool SetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask);
/** Checks if the algorithm supports masks of the passed type.*/
bool CheckSupport(const mitk::Image* movingMask, const mitk::Image* targetMask) const;
static bool HasMaskedRegistrationAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm);
~MaskedAlgorithmHelper() {}
private:
using MaskPixelType = unsigned char;
MaskedAlgorithmHelper& operator = (const MaskedAlgorithmHelper&);
MaskedAlgorithmHelper(const MaskedAlgorithmHelper&);
/**Internal helper that is used by SetMasks if the data are images to set them properly.*/
template<unsigned int VImageDimension1, unsigned int VImageDimension2>
bool DoSetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask);
/**Internal helper that is used by SetData if the data are images to cast and set them properly.*/
template<typename TPixelType, unsigned int VImageDimension>
void DoConvertMask(const itk::Image<TPixelType, VImageDimension>* mask);
/**Internal helper that is used by SetData if the data are images to set them properly.*/
template<unsigned int VImageDimension>
void DoConvertMask(const itk::Image<MaskPixelType, VImageDimension>* mask);
/**Internal helper that is used to pack the mask image into a spatial object.*/
template<unsigned int VImageDimension>
typename itk::SpatialObject<VImageDimension>::Pointer ConvertMaskSO(const itk::Image<MaskPixelType, VImageDimension>* mask) const;
- /**Helper member that containes the result of the last call of DoConvertMask().*/
+ /**Helper member that contains the result of the last call of DoConvertMask().*/
itk::DataObject::Pointer m_convertResult;
map::algorithm::RegistrationAlgorithmBase::Pointer m_AlgorithmBase;
};
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h b/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h
index 97205e05fd..9354f61372 100644
--- a/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h
+++ b/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h
@@ -1,44 +1,44 @@
/*============================================================================
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 mitkMatchPointPropertyTags_h
#define mitkMatchPointPropertyTags_h
// MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
- /**UID of the algorithm that was used to determin a registration.*/
+ /**UID of the algorithm that was used to determine a registration.*/
const char* const Prop_RegAlgUsed = "matchpoint.Registration.Algorithm.UID";
/**UID(s) of the data object(s) used as target for determining the registration.*/
const char* const Prop_RegAlgTargetData = "matchpoint.Registration.Algorithm.UsedData.target";
/**UID(s) of the data object(s) used as moving objects for determining the registration.*/
const char* const Prop_RegAlgMovingData = "matchpoint.Registration.Algorithm.UsedData.moving";
/**UID of the registration instance.*/
const char* const Prop_RegUID = "matchpoint.Registration.UID";
/**Input "section" that specifies what wwas mapped.*/
const char* const Prop_MappingInput = "matchpoint.Mapping.Input";
/**UID of the data object that was mapped (so the source) by the specified registration to generate the current instance.*/
const char* const Prop_MappingInputData = "matchpoint.Mapping.Input.Data";
/**Type of the interpolation strategy that was used to map the object. If not set, no interpolation was needed for mapping.*/
const char* const Prop_MappingInterpolator = "matchpoint.Mapping.Interpolator";
/**Indicates that the data was not mapped (in termes of resampled), but "just" the geometry was refined.*/
const char* const Prop_MappingRefinedGeometry = "matchpoint.Mapping.RefinedGeometry";
/**MatchPoint UID to uniquely identify an data object.*/
const char* const Prop_UID = "data.UID";
/**MatchPoint UID to uniquely identify an node.*/
const char* const nodeProp_UID = "matchpoint.UID";
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h b/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h
index ddd8ca2093..84f8480984 100644
--- a/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h
@@ -1,60 +1,60 @@
/*============================================================================
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 mitkPointSetMappingHelper_h
#define mitkPointSetMappingHelper_h
#include "mapRegistrationBase.h"
#include <mitkPointSet.h>
#include "mitkMAPRegistrationWrapper.h"
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
namespace PointSetMappingHelper
{
typedef ::map::core::RegistrationBase RegistrationType;
typedef ::mitk::MAPRegistrationWrapper MITKRegistrationType;
/**Helper that converts the data of an mitk point set into the default point set type of matchpoint.*/
MITKMATCHPOINTREGISTRATION_EXPORT ::map::core::continuous::Elements<3>::InternalPointSetType::Pointer ConvertPointSetMITKtoMAP(const mitk::PointSet::DataType* mitkSet);
/**Helper that maps a given input point set
* @param input Point set that should be mapped.
* @param registration Pointer to the registration instance that should be used for mapping
* @param timeStep Indicates which time step of the point set should be mapped (the rest will just be copied). -1 (default) indicates that all time steps should be mapped.
* @param throwOnMappingError Indicates if mapping should fail with an exception (true), if the registration does not cover/support the whole requested region for mapping into the result image.
- * if set to false, points that cause an mapping error will be transfered without mapping but get the passed errorPointValue as data to indicate unmappable points;
+ * if set to false, points that cause an mapping error will be transferred without mapping but get the passed errorPointValue as data to indicate unmappable points;
* @param errorPointValue Indicates the point data that should be used if an mapping error occurs (and throwOnMappingError is false).
* @pre input must be valid
* @pre registration must be valid
* @pre timeStep must be a valid time step of input or -1
* @pre Dimensionality of the registration must match with the input imageinput must be valid
* @remark Depending in the settings of throwOnMappingError it may also throw
* due to inconsistencies in the mapping process. See parameter description.
* @result Pointer to the resulting mapped point set*/
MITKMATCHPOINTREGISTRATION_EXPORT ::mitk::PointSet::Pointer map(const ::mitk::PointSet* input, const RegistrationType* registration, int timeStep = -1,
bool throwOnMappingError = true, const ::mitk::PointSet::PointDataType& errorPointValue = ::mitk::PointSet::PointDataType());
/**Helper that maps a given input point set
* @overload*/
MITKMATCHPOINTREGISTRATION_EXPORT ::mitk::PointSet::Pointer map(const ::mitk::PointSet* input, const MITKRegistrationType* registration, int timeStep = -1,
bool throwOnMappingError = true, const ::mitk::PointSet::PointDataType& errorPointValue = ::mitk::PointSet::PointDataType());
}
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h b/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h
index dad1a40a4b..376bb10367 100644
--- a/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h
+++ b/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h
@@ -1,75 +1,75 @@
/*============================================================================
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 mitkQMAPAlgorithmModel_h
#define mitkQMAPAlgorithmModel_h
#include <QAbstractTableModel>
#include <QStringList>
//MITK
#include "MitkMatchPointRegistrationExports.h"
// MatchPoint
#include <mapRegistrationAlgorithmBase.h>
#include <mapMetaPropertyAlgorithmInterface.h>
namespace mitk
{
/*!
\class QMAPAlgorithmModel
Helper class that implements a model to handle the MetaProperty interface of a MatchPoint algorithm
- in contect of the QT view-model-concept. A algorithm can be set as data source for the model.
+ in context of the QT view-model-concept. A algorithm can be set as data source for the model.
The model retrieves all information through the MetaPropertyInterface. Changes in the view will
be propagated by the model into the algorithm.
\remarks The model only keep a simple pointer to the MetaPropertyInterface of the algorithm.
You have to ensure to reset the algorithm if the pointer goes invalid.
\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT QMAPAlgorithmModel : public QAbstractTableModel
{
Q_OBJECT
public:
QMAPAlgorithmModel(QObject *parent = nullptr);
virtual ~QMAPAlgorithmModel() {};
void SetAlgorithm(map::algorithm::RegistrationAlgorithmBase *pAlgorithm);
void SetAlgorithm(map::algorithm::facet::MetaPropertyAlgorithmInterface *pMetaInterface);
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
private:
void UpdateMetaProperties() const ;
/** Method uses m_pMetaInterface to retrieve the MetaProperty and unwraps it into an
* suitable QVariant depending on the passed QT role. If the MetaProperty type is not supported, the QVariant is invalid.
*/
QVariant GetPropertyValue(const map::algorithm::MetaPropertyInfo* pInfo, int role) const;
template <typename TValueType> bool CheckCastAndSetProp(const map::algorithm::MetaPropertyInfo* pInfo, const QVariant& value);
bool SetPropertyValue(const map::algorithm::MetaPropertyInfo* pInfo, const QVariant& value);
map::algorithm::facet::MetaPropertyAlgorithmInterface *m_pMetaInterface;
mutable map::algorithm::facet::MetaPropertyAlgorithmInterface::MetaPropertyVectorType m_MetaProperties;
};
};
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h b/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h
index b977c4d5be..978cfdaab3 100644
--- a/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h
+++ b/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h
@@ -1,248 +1,248 @@
/*============================================================================
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 mitkRegEvaluationMapper2D_h
#define mitkRegEvaluationMapper2D_h
//MatchPoint
#include <mapRegistration.h>
#include "mitkRegEvaluationObject.h"
//MITK
#include <mitkCommon.h>
//MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkVtkMapper.h"
#include "mitkExtractSliceFilter.h"
//VTK
#include <vtkSmartPointer.h>
#include <vtkPropAssembly.h>
//MITK
#include "MitkMatchPointRegistrationExports.h"
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageExtractComponents;
class vtkImageReslice;
class vtkImageChangeInformation;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkMitkApplyLevelWindowToRGBFilter;
class vtkMitkLevelWindowFilter;
namespace mitk {
/** \brief Mapper to resample and display 2D slices of registration evaluation visualization.
* \ingroup Mapper
*/
class MITKMATCHPOINTREGISTRATION_EXPORT RegEvaluationMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro( RegEvaluationMapper2D,VtkMapper );
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const mitk::DataNode* GetTargetNode(void);
const mitk::DataNode* GetMovingNode(void);
/** \brief Get the target image to map */
const mitk::Image *GetTargetImage(void);
/** \brief Get the moving image to map */
const mitk::Image *GetMovingImage(void);
/** \brief Get the target image to map */
const mitk::MAPRegistrationWrapper *GetRegistration(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer * renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKMATCHPOINTREGISTRATION_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
/** \brief Actor of a 2D render window. */
vtkSmartPointer<vtkActor> m_Actor;
vtkSmartPointer<vtkPropAssembly> m_Actors;
/** \brief Mapper of a 2D render window. */
vtkSmartPointer<vtkPolyDataMapper> m_Mapper;
/** \brief Current slice of a 2D render window.*/
vtkSmartPointer<vtkImageData> m_EvaluationImage;
/** \brief Empty vtkPolyData that is set when rendering geometry does not
* intersect the image geometry.
* \warning This member variable is set to nullptr,
* if no image geometry is inside the plane geometry
* of the respective render window. Any user of this
* slice has to check whether it is set to nullptr!
*/
vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
/** \brief Plane on which the slice is rendered as texture. */
vtkSmartPointer<vtkPlaneSource> m_Plane;
/** \brief The texture which is used to render the current slice. */
vtkSmartPointer<vtkTexture> m_Texture;
/** \brief The lookuptables for colors and level window */
vtkSmartPointer<vtkLookupTable> m_ColorLookupTable;
vtkSmartPointer<vtkLookupTable> m_DefaultLookupTable;
/** \brief The actual reslicer (one per renderer) */
mitk::ExtractSliceFilter::Pointer m_Reslicer;
/** part of the target image that is relevant for the rendering*/
mitk::Image::Pointer m_slicedTargetImage;
/** part of the moving image mapped into the slicedTargetImage
geometry*/
mitk::Image::Pointer m_slicedMappedImage;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType* m_mmPerPixel;
/** \brief This filter is used to apply the level window to target image. */
vtkSmartPointer<vtkMitkLevelWindowFilter> m_TargetLevelWindowFilter;
/** \brief This filter is used to apply the level window to moving image. */
vtkSmartPointer<vtkMitkLevelWindowFilter> m_MappedLevelWindowFilter;
vtkSmartPointer<vtkImageExtractComponents> m_TargetExtractFilter;
vtkSmartPointer<vtkImageExtractComponents> m_MappedExtractFilter;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default deconstructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage* GetLocalStorage(mitk::BaseRenderer* renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = nullptr, bool overwrite = false);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer* renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]);
/** Default constructor */
RegEvaluationMapper2D();
/** Default deconstructor */
~RegEvaluationMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
void PrepareContour( mitk::DataNode* datanode, LocalStorage * localStorage );
void PrepareDifference( LocalStorage * localStorage );
void PrepareWipe(mitk::DataNode* datanode, LocalStorage * localStorage, const Point2D& currentIndex2D);
void PrepareCheckerBoard( mitk::DataNode* datanode, LocalStorage * localStorage );
void PrepareColorBlend( LocalStorage * localStorage );
void PrepareBlend( mitk::DataNode* datanode, LocalStorage * localStorage );
/** \brief This method uses the vtkCamera clipping range and the layer property
- * to calcualte the depth of the object (e.g. image or contour). The depth is used
+ * to calculate the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer* renderer);
/** \brief This method applies (or modifies) the lookuptable for all types of images.
* \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode'
* which uses the lookup table must be set.
*/
void ApplyLookuptable(mitk::BaseRenderer* renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter);
/**
* @brief ApplyLevelWindow Apply the level window for the given renderer.
* \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses the level window must be set.
* @param renderer Level window for which renderer?
* @param dataNode
* @param levelFilter
*/
void ApplyLevelWindow(mitk::BaseRenderer *renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter);
/** \brief Set the opacity of the actor. */
void ApplyOpacity( mitk::BaseRenderer* renderer );
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given PlaneGeometry intersects the given
* SlicedGeometry3D. It calculates the distance of the PlaneGeometry to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
*
* \param renderingGeometry
* \param imageGeometry
**/
bool RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry );
};
} // namespace mitk
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h b/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h
index d7a4f59488..1e4a2c1b74 100644
--- a/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h
+++ b/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h
@@ -1,111 +1,111 @@
/*============================================================================
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 mitkRegEvaluationObject_h
#define mitkRegEvaluationObject_h
//MITK
#include <mitkImage.h>
#include <mitkDataNode.h>
//MatchPoint
#include <mapRegistrationBase.h>
#include <mapRegistration.h>
#include <mapExceptionObjectMacros.h>
#include <mapContinuousElements.h>
//MITK
#include "mitkMAPRegistrationWrapper.h"
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/*!
\brief RegEvaluationObject
- Class that containes all data to realize an evaluation of registrations via images.
+ Class that contains all data to realize an evaluation of registrations via images.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT RegEvaluationObject: public mitk::BaseData
{
public:
mitkClassMacro( RegEvaluationObject, BaseData );
itkNewMacro( Self );
/**
* Pass through to the target image that defines the region
*/
void SetRequestedRegionToLargestPossibleRegion() override;
/**
* Pass through to the target image that defines the region
*/
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/**
* Pass through to the target image that defines the region
*/
bool VerifyRequestedRegion() override;
/**
* Pass through to the target image that defines the region
*/
void SetRequestedRegion(const itk::DataObject*) override;
itkSetObjectMacro(Registration, mitk::MAPRegistrationWrapper);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as target image*/
void SetTargetImage(const mitk::Image* tImg);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as mapped moving*/
void SetMovingImage(const mitk::Image* mImg);
itkGetObjectMacro(Registration, mitk::MAPRegistrationWrapper);
itkGetObjectMacro(TargetImage, mitk::Image);
itkGetObjectMacro(MovingImage, mitk::Image);
itkGetConstObjectMacro(Registration, mitk::MAPRegistrationWrapper);
itkGetConstObjectMacro(TargetImage, mitk::Image);
itkGetConstObjectMacro(MovingImage, mitk::Image);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as target image*/
void SetTargetNode(const mitk::DataNode* tNode);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as mapped moving*/
void SetMovingNode(const mitk::DataNode* mNode);
itkGetConstObjectMacro(TargetNode, mitk::DataNode);
itkGetConstObjectMacro(MovingNode, mitk::DataNode);
protected:
typedef ::itk::Image<unsigned char, 3> InternalImageType;
template <typename TPixelType, unsigned int VImageDimension >
void doConversion(const ::itk::Image<TPixelType,VImageDimension>* input, mitk::Image::Pointer& result) const;
void PrintSelf (std::ostream &os, itk::Indent indent) const override;
RegEvaluationObject();
~RegEvaluationObject() override;
mitk::MAPRegistrationWrapper::Pointer m_Registration;
mitk::Image::Pointer m_TargetImage;
mitk::Image::Pointer m_MovingImage;
mitk::DataNode::ConstPointer m_TargetNode;
mitk::DataNode::ConstPointer m_MovingNode;
private:
RegEvaluationObject& operator = (const RegEvaluationObject&);
RegEvaluationObject(const RegEvaluationObject&);
};
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkRegVisHelper.h b/Modules/MatchPointRegistration/include/mitkRegVisHelper.h
index 7fecb3d09f..3a38098422 100644
--- a/Modules/MatchPointRegistration/include/mitkRegVisHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkRegVisHelper.h
@@ -1,77 +1,77 @@
/*============================================================================
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 mitkRegVisHelper_h
#define mitkRegVisHelper_h
//VTK
#include <vtkSmartPointer.h>
#include <vtkPolyData.h>
//MITK
#include <mitkDataNode.h>
#include <mitkGeometry3D.h>
//MatchPoint
#include <mapRegistrationKernelBase.h>
// MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/** Generates the geometry info used to visualized a registration based on the properties
* of the data node containing the registration.\n
* @pre regNode is a correctly initialized data node of a registration
* @param regNode Pointer to the data node of the registration.
* @param [out] gridDesc Smartpointer to the extracted grid geometry.
* @param [out] gridFrequ Grid frequency stored in the regNode.
*/
void MITKMATCHPOINTREGISTRATION_EXPORT GetGridGeometryFromNode(const mitk::DataNode* regNode, mitk::Geometry3D::Pointer& gridDesc, unsigned int& gridFrequ);
/**
- * Generates a 3D defomration gird according to a passed Geometry3D info. It is the basis
+ * Generates a 3D defomration grid according to a passed Geometry3D info. It is the basis
* for most of the visualizations of a MatchPoint registration.
*/
vtkSmartPointer<vtkPolyData> MITKMATCHPOINTREGISTRATION_EXPORT Generate3DDeformationGrid(const mitk::BaseGeometry* gridDesc, unsigned int gridFrequence, const map::core::RegistrationKernelBase<3,3>* regKernel = nullptr);
/**
* Generates a 3D glyph representation of the given regKernel in the FOV defined by gridDesc.
*/
vtkSmartPointer<vtkPolyData> MITKMATCHPOINTREGISTRATION_EXPORT Generate3DDeformationGlyph(const mitk::BaseGeometry* gridDesc, const map::core::RegistrationKernelBase<3,3>* regKernel);
/**
* Checks if the grid relevant node properties are outdated regarding the passed time stamp
* reference*/
bool MITKMATCHPOINTREGISTRATION_EXPORT GridIsOutdated(const mitk::DataNode* regNode, const itk::TimeStamp& reference);
/**
* Checks if the property of the passed node is outdated regarding the passed time stamp
* reference
* If the property does not exist the return value indicates if the node is outdated.*/
bool MITKMATCHPOINTREGISTRATION_EXPORT PropertyIsOutdated(const mitk::DataNode* regNode, const std::string& propName, const itk::TimeStamp& reference);
/**
* Gets the relevant kernel for visualization of a registration node. The kernel is determined
* by the direction property of the node.
* @return Pointer to the relevant kernel. Method may return nullptr if data node is not valid, node
* contains no registration or has no direction property.*/
MITKMATCHPOINTREGISTRATION_EXPORT const map::core::RegistrationKernelBase<3,3>* GetRelevantRegKernelOfNode(const mitk::DataNode* regNode);
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.h b/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.h
index 197424070a..4d400dbd33 100644
--- a/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.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 mitkTimeFramesRegistrationHelper_h
#define mitkTimeFramesRegistrationHelper_h
#include <mitkImage.h>
#include <mitkTimeGeometry.h>
#include <mitkImageMappingHelper.h>
#include <mapRegistrationAlgorithmBase.h>
#include <mapRegistrationBase.h>
#include <mapEvents.h>
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
mapEventMacro(FrameRegistrationEvent, ::map::events::TaskBatchEvent, MITKMATCHPOINTREGISTRATION_EXPORT);
mapEventMacro(FrameMappingEvent, ::map::events::TaskBatchEvent, MITKMATCHPOINTREGISTRATION_EXPORT);
- /** Helper class that assumes that registeres time frames of an passed image and returns the resulting new image.
+ /** Helper class that assumes that registers time frames of an passed image and returns the resulting new image.
* A typical use case for the helper class is motion correction in 3D+t images. Each selected frame will be registered
* to the first frame of the image. The user can define frames that may be not registered. These frames will be copied directly.
* Per default all frames will be registered.
- * The user may set a mask for the target frame (1st frame). If this mask image has mulitple time steps, the first time step will be used.
+ * The user may set a mask for the target frame (1st frame). If this mask image has multiple time steps, the first time step will be used.
* The helper class invokes three eventtypes: \n
* - mitk::FrameRegistrationEvent: when ever a frame was registered.
* - mitk::FrameMappingEvent: when ever a frame was mapped registered.
* - itk::ProgressEvent: when ever a new frame was added to the result image.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT TimeFramesRegistrationHelper : public itk::Object
{
public:
mitkClassMacroItkParent(TimeFramesRegistrationHelper, itk::Object);
itkNewMacro(Self);
typedef ::map::algorithm::RegistrationAlgorithmBase RegistrationAlgorithmBaseType;
typedef RegistrationAlgorithmBaseType::Pointer RegistrationAlgorithmPointer;
typedef ::map::core::RegistrationBase RegistrationType;
typedef RegistrationType::Pointer RegistrationPointer;
typedef std::vector<mitk::TimeStepType> IgnoreListType;
itkSetConstObjectMacro(4DImage, Image);
itkGetConstObjectMacro(4DImage, Image);
itkSetConstObjectMacro(TargetMask, Image);
itkGetConstObjectMacro(TargetMask, Image);
itkSetObjectMacro(Algorithm, RegistrationAlgorithmBaseType);
itkGetObjectMacro(Algorithm, RegistrationAlgorithmBaseType);
itkSetMacro(AllowUndefPixels, bool);
itkGetConstMacro(AllowUndefPixels, bool);
itkSetMacro(PaddingValue, double);
itkGetConstMacro(PaddingValue, double);
itkSetMacro(AllowUnregPixels, bool);
itkGetConstMacro(AllowUnregPixels, bool);
itkSetMacro(ErrorValue, double);
itkGetConstMacro(ErrorValue, double);
itkSetMacro(InterpolatorType, mitk::ImageMappingInterpolator::Type);
itkGetConstMacro(InterpolatorType, mitk::ImageMappingInterpolator::Type);
- /** cleares the ignore list. Therefore all frames will be processed.*/
+ /** Clears the ignore list. Therefore all frames will be processed.*/
void ClearIgnoreList();
void SetIgnoreList(const IgnoreListType& il);
itkGetConstMacro(IgnoreList, IgnoreListType);
virtual double GetProgress() const;
/** Commences the generation of the registered 4D image. Stores the result internally.
* After this method call is finished the result can be retrieved via
* GetRegisteredImage.
* @pre 4D image must be set
* @pre 4D image must has more then one frame
* @pre Reg algorithm must be set
* @pre Ignore list values must be within the time geometry of the image
* @post Result image was generated.*/
void Generate();
/** Returns the generated images. Triggers Generate() if result is outdated.
* @pre 4D image must be set
* @pre 4D image must has more then one frame
* @pre Reg algorithm must be set
* @pre Ignore list values must be within the time geometry of the image
*/
Image::Pointer GetRegisteredImage();
protected:
TimeFramesRegistrationHelper() :
m_AllowUndefPixels(true),
m_PaddingValue(0),
m_AllowUnregPixels(true),
m_ErrorValue(0),
m_InterpolatorType(mitk::ImageMappingInterpolator::Linear),
m_Progress(0)
{
m_4DImage = nullptr;
m_TargetMask = nullptr;
m_Registered4DImage = nullptr;
};
~TimeFramesRegistrationHelper() override {};
RegistrationPointer DoFrameRegistration(const mitk::Image* movingFrame,
const mitk::Image* targetFrame, const mitk::Image* targetMask) const;
mitk::Image::Pointer DoFrameMapping(const mitk::Image* movingFrame, const RegistrationType* reg,
const mitk::Image* targetFrame) const;
bool HasOutdatedResult() const;
/** Check if the fit can be generated and all needed inputs are valid.
* Throw an exception for a non valid or missing input.*/
void CheckValidInputs() const;
mitk::Image::Pointer GetFrameImage(const mitk::Image* image, mitk::TimePointType timePoint) const;
RegistrationAlgorithmPointer m_Algorithm;
private:
Image::ConstPointer m_4DImage;
Image::ConstPointer m_TargetMask;
Image::Pointer m_Registered4DImage;
IgnoreListType m_IgnoreList;
/**Indicates if the mapper should allow undefined pixels (true) or mapping should fail (false)*/
bool m_AllowUndefPixels;
/** Value of undefined pixels. Only relevant if m_allowUndefPixels is true. */
double m_PaddingValue;
/**Indicates if the mapper should allow pixels that are not covered by the registration (true) or mapping should fail (false)*/
bool m_AllowUnregPixels;
/** Value of unreged pixels. Only relevant if m_allowUnregPixels is true. */
double m_ErrorValue;
/** Type of interpolator. Only relevant for images and if m_doGeometryRefinement is false. */
mitk::ImageMappingInterpolator::Type m_InterpolatorType;
double m_Progress;
};
}
#endif
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp
index 58035fabbf..f2f311aa51 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp
@@ -1,482 +1,482 @@
/*============================================================================
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 <itkInterpolateImageFunction.h>
#include <itkNearestNeighborInterpolateImageFunction.h>
#include <itkLinearInterpolateImageFunction.h>
#include <itkBSplineInterpolateImageFunction.h>
#include <itkWindowedSincInterpolateImageFunction.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkGeometry3D.h>
#include <mitkImageToItk.h>
#include <mitkImageTimeSelector.h>
#include <mitkLabelSetImage.h>
#include "mapRegistration.h"
#include "mitkImageMappingHelper.h"
#include "mitkRegistrationHelper.h"
template <typename TImage >
typename ::itk::InterpolateImageFunction< TImage >::Pointer generateInterpolator(mitk::ImageMappingInterpolator::Type interpolatorType)
{
typedef ::itk::InterpolateImageFunction< TImage > BaseInterpolatorType;
typename BaseInterpolatorType::Pointer result;
switch (interpolatorType)
{
case mitk::ImageMappingInterpolator::NearestNeighbor:
{
result = ::itk::NearestNeighborInterpolateImageFunction<TImage>::New();
break;
}
case mitk::ImageMappingInterpolator::BSpline_3:
{
typename ::itk::BSplineInterpolateImageFunction<TImage>::Pointer spInterpolator = ::itk::BSplineInterpolateImageFunction<TImage>::New();
spInterpolator->SetSplineOrder(3);
result = spInterpolator;
break;
}
case mitk::ImageMappingInterpolator::WSinc_Hamming:
{
result = ::itk::WindowedSincInterpolateImageFunction<TImage,4>::New();
break;
}
case mitk::ImageMappingInterpolator::WSinc_Welch:
{
result = ::itk::WindowedSincInterpolateImageFunction<TImage,4,::itk::Function::WelchWindowFunction<4> >::New();
break;
}
default:
{
result = ::itk::LinearInterpolateImageFunction<TImage>::New();
break;
}
}
return result;
};
template <typename TPixelType, unsigned int VImageDimension >
void doMITKMap(const ::itk::Image<TPixelType,VImageDimension>* input, mitk::ImageMappingHelper::ResultImageType::Pointer& result, const mitk::ImageMappingHelper::RegistrationType*& registration,
bool throwOnOutOfInputAreaError, const double& paddingValue, const mitk::ImageMappingHelper::ResultImageGeometryType*& resultGeometry,
bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type interpolatorType)
{
typedef ::map::core::Registration<VImageDimension,VImageDimension> ConcreteRegistrationType;
typedef ::map::core::ImageMappingTask<ConcreteRegistrationType, ::itk::Image<TPixelType,VImageDimension>, ::itk::Image<TPixelType,VImageDimension> > MappingTaskType;
typename MappingTaskType::Pointer spTask = MappingTaskType::New();
typedef typename MappingTaskType::ResultImageDescriptorType ResultImageDescriptorType;
typename ResultImageDescriptorType::Pointer resultDescriptor;
//check if image and result geometry fits the passed registration
/////////////////////////////////////////////////////////////////
if (registration->getMovingDimensions()!=VImageDimension)
{
map::core::OStringStream str;
str << "Dimension of MITK image ("<<VImageDimension<<") does not equal the moving dimension of the registration object ("<<registration->getMovingDimensions()<<").";
throw mitk::AccessByItkException(str.str());
}
if (registration->getTargetDimensions()!=VImageDimension)
{
map::core::OStringStream str;
str << "Dimension of MITK image ("<<VImageDimension<<") does not equal the target dimension of the registration object ("<<registration->getTargetDimensions()<<").";
throw mitk::AccessByItkException(str.str());
}
const ConcreteRegistrationType* castedReg = dynamic_cast<const ConcreteRegistrationType*>(registration);
if (registration->getTargetDimensions()==2 && resultGeometry)
{
mitk::ImageMappingHelper::ResultImageGeometryType::BoundsArrayType bounds = resultGeometry->GetBounds();
if (bounds[4]!=0 || bounds[5]!=0)
{
//array "bounds" is constructed as [min Dim1, max Dim1, min Dim2, max Dim2, min Dim3, max Dim3]
- //therfore [4] and [5] must be 0
+ //therefore [4] and [5] must be 0
map::core::OStringStream str;
str << "Dimension of defined result geometry does not equal the target dimension of the registration object ("<<registration->getTargetDimensions()<<").";
throw mitk::AccessByItkException(str.str());
}
}
//check/create resultDescriptor
/////////////////////////
if (resultGeometry)
{
resultDescriptor = ResultImageDescriptorType::New();
typename ResultImageDescriptorType::PointType origin;
typename ResultImageDescriptorType::SizeType size;
typename ResultImageDescriptorType::SpacingType fieldSpacing;
typename ResultImageDescriptorType::DirectionType matrix;
mitk::ImageMappingHelper::ResultImageGeometryType::BoundsArrayType geoBounds = resultGeometry->GetBounds();
mitk::Vector3D geoSpacing = resultGeometry->GetSpacing();
mitk::Point3D geoOrigin = resultGeometry->GetOrigin();
mitk::AffineTransform3D::MatrixType geoMatrix = resultGeometry->GetIndexToWorldTransform()->GetMatrix();
for (unsigned int i = 0; i<VImageDimension; ++i)
{
origin[i] = static_cast<typename ResultImageDescriptorType::PointType::ValueType>(geoOrigin[i]);
fieldSpacing[i] = static_cast<typename ResultImageDescriptorType::SpacingType::ValueType>(geoSpacing[i]);
size[i] = static_cast<typename ResultImageDescriptorType::SizeType::SizeValueType>(geoBounds[(2*i)+1]-geoBounds[2*i])*fieldSpacing[i];
}
//Matrix extraction
matrix.SetIdentity();
unsigned int i;
unsigned int j;
/// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix.
/// If it is only a rotation around the transversal 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.
+ // the following loop divides by spacing now to normalize columns.
// counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092.
// Check if information is lost
if ( VImageDimension == 2)
{
if ( ( geoMatrix[0][2] != 0) ||
( geoMatrix[1][2] != 0) ||
( geoMatrix[2][0] != 0) ||
( geoMatrix[2][1] != 0) ||
(( geoMatrix[2][2] != 1) && ( geoMatrix[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 < 2; ++i)
{
for( j=0; j < 2; ++j )
{
matrix[i][j] = geoMatrix[i][j]/fieldSpacing[j];
}
}
}
}
else if (VImageDimension == 3)
{
// Normal 3D image. Conversion possible without problem!
for ( i=0; i < 3; ++i)
{
for( j=0; j < 3; ++j )
{
matrix[i][j] = geoMatrix[i][j]/fieldSpacing[j];
}
}
}
else
{
assert(0);
throw mitk::AccessByItkException("Usage of resultGeometry for 2D images is not yet implemented.");
/**@TODO Implement extraction of 2D-Rotation-Matrix out of 3D-Rotation-Matrix
* to cover this case as well.
* matrix = extract2DRotationMatrix(resultGeometry)*/
}
resultDescriptor->setOrigin(origin);
resultDescriptor->setSize(size);
resultDescriptor->setSpacing(fieldSpacing);
resultDescriptor->setDirection(matrix);
}
//do the mapping
/////////////////////////
typedef ::itk::InterpolateImageFunction< ::itk::Image<TPixelType,VImageDimension> > BaseInterpolatorType;
typename BaseInterpolatorType::Pointer interpolator = generateInterpolator< ::itk::Image<TPixelType,VImageDimension> >(interpolatorType);
assert(interpolator.IsNotNull());
spTask->setImageInterpolator(interpolator);
spTask->setInputImage(input);
spTask->setRegistration(castedReg);
spTask->setResultImageDescriptor(resultDescriptor);
spTask->setThrowOnMappingError(throwOnMappingError);
spTask->setErrorValue(errorValue);
spTask->setThrowOnPaddingError(throwOnOutOfInputAreaError);
spTask->setPaddingValue(paddingValue);
spTask->execute();
mitk::CastToMitkImage<>(spTask->getResultImage(),result);
}
/**Helper function to ensure the mapping of all time steps of an image.*/
void doMapTimesteps(const mitk::ImageMappingHelper::InputImageType* input, mitk::Image* result, const mitk::ImageMappingHelper::RegistrationType* registration, bool throwOnOutOfInputAreaError,double paddingValue, const mitk::ImageMappingHelper::ResultImageGeometryType* resultGeometry, bool throwOnMappingError, double errorValue, mitk::ImageMappingInterpolator::Type interpolatorType)
{
for (unsigned int i = 0; i<input->GetTimeSteps(); ++i)
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(input);
imageTimeSelector->SetTimeNr(i);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::ImageMappingHelper::InputImageType::Pointer timeStepInput = imageTimeSelector->GetOutput();
mitk::ImageMappingHelper::ResultImageType::Pointer timeStepResult;
AccessByItk_n(timeStepInput, doMITKMap, (timeStepResult, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType));
mitk::ImageReadAccessor readAccess(timeStepResult);
result->SetVolume(readAccess.GetData(), i);
}
}
mitk::TimeGeometry::Pointer CreateResultTimeGeometry(const mitk::ImageMappingHelper::InputImageType* input, const mitk::ImageMappingHelper::ResultImageGeometryType* resultGeometry)
{
mitk::TimeGeometry::ConstPointer timeGeometry = input->GetTimeGeometry();
mitk::TimeGeometry::Pointer mappedTimeGeometry = timeGeometry->Clone();
for (unsigned int i = 0; i < input->GetTimeSteps(); ++i)
{
mitk::ImageMappingHelper::ResultImageGeometryType::Pointer mappedGeometry = resultGeometry->Clone();
mappedTimeGeometry->SetTimeStepGeometry(mappedGeometry, i);
}
return mappedTimeGeometry;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::map(const InputImageType* input, const RegistrationType* registration,
bool throwOnOutOfInputAreaError, const double& paddingValue, const ResultImageGeometryType* resultGeometry,
bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type interpolatorType)
{
if (!registration)
{
mitkThrow() << "Cannot map image. Passed registration wrapper pointer is nullptr.";
}
if (!input)
{
mitkThrow() << "Cannot map image. Passed image pointer is nullptr.";
}
ResultImageType::Pointer result;
auto inputLabelSetImage = dynamic_cast<const LabelSetImage*>(input);
if (nullptr == inputLabelSetImage)
{
if (input->GetTimeSteps() == 1)
{ //map the image and done
AccessByItk_n(input, doMITKMap, (result, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType));
}
else
{ //map every time step and compose
auto mappedTimeGeometry = CreateResultTimeGeometry(input, resultGeometry);
result = mitk::Image::New();
result->Initialize(input->GetPixelType(), *mappedTimeGeometry, 1, input->GetTimeSteps());
doMapTimesteps(input, result, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType);
}
}
else
{
auto resultLabelSetImage = LabelSetImage::New();
auto mappedTimeGeometry = CreateResultTimeGeometry(input, resultGeometry);
auto resultTemplate = mitk::Image::New();
resultTemplate->Initialize(input->GetPixelType(), *mappedTimeGeometry, 1, input->GetTimeSteps());
resultLabelSetImage->Initialize(resultTemplate);
auto cloneInput = inputLabelSetImage->Clone();
//We need to clone the LabelSetImage due to its illposed design. It is state full
//and we have to iterate through all layers as active layers to ensure the content
//was really stored (directly working with the layer images does not work with the
- //active layer). The clone wastes rescources but is the easiest and safest way to
+ //active layer). The clone wastes resources but is the easiest and safest way to
//ensure 1) correct mapping 2) avoid race conditions with other parts of the
//application because we would change the state of the input.
//This whole code block should be reworked as soon as T28525 is done.
for (unsigned int layerID = 0; layerID < inputLabelSetImage->GetNumberOfLayers(); ++layerID)
{
if (resultLabelSetImage->GetNumberOfLayers() <= layerID)
{
resultLabelSetImage->AddLayer();
}
resultLabelSetImage->ReplaceGroupLabels(layerID, inputLabelSetImage->GetConstLabelsByValue(inputLabelSetImage->GetLabelValuesByGroup(layerID)));
cloneInput->SetActiveLayer(layerID);
resultLabelSetImage->SetActiveLayer(layerID);
doMapTimesteps(cloneInput, resultLabelSetImage, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, mitk::ImageMappingInterpolator::Linear);
}
resultLabelSetImage->SetActiveLayer(inputLabelSetImage->GetActiveLayer());
resultLabelSetImage->SetActiveLabel(inputLabelSetImage->GetActiveLabel()->GetValue());
result = resultLabelSetImage;
}
return result;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::map(const InputImageType* input, const MITKRegistrationType* registration,
bool throwOnOutOfInputAreaError, const double& paddingValue, const ResultImageGeometryType* resultGeometry,
bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type)
{
if (!registration)
{
mitkThrow() << "Cannot map image. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot map image. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot map image. Passed registration wrapper contains no registration.";
}
if (!input)
{
mitkThrow() << "Cannot map image. Passed image pointer is nullptr.";
}
ResultImageType::Pointer result = map(input, registration->GetRegistration(), throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue);
return result;
}
mitk::ImageMappingHelper::ResultImageGeometryType::Pointer
mitk::ImageMappingHelper::GenerateSuperSampledGeometry(const ResultImageGeometryType* inputGeometry, double xScaling, double yScaling, double zScaling)
{
auto resultGeometry = inputGeometry->Clone();
//change the pixel count and spacing of the geometry
mitk::BaseGeometry::BoundsArrayType geoBounds = inputGeometry->GetBounds();
auto oldSpacing = inputGeometry->GetSpacing();
mitk::Vector3D geoSpacing;
geoSpacing[0] = oldSpacing[0] / xScaling;
geoSpacing[1] = oldSpacing[1] / yScaling;
geoSpacing[2] = oldSpacing[2] / zScaling;
geoBounds[1] = geoBounds[1] * xScaling;
geoBounds[3] = geoBounds[3] * yScaling;
geoBounds[5] = geoBounds[5] * zScaling;
resultGeometry->SetBounds(geoBounds);
resultGeometry->SetSpacing(geoSpacing);
auto oldOrigin = inputGeometry->GetOrigin();
//if we change the spacing we must also correct the origin to ensure
//that the voxel matrix still covers the same space. This is due the fact
//that the origin is not in the corner of the voxel matrix, but in the center
// of the voxel that is in the corner.
mitk::Point3D newOrigin;
for (mitk::Point3D::SizeType i = 0; i < 3; ++i)
{
newOrigin[i] = 0.5 * (geoSpacing[i] - oldSpacing[i]) + oldOrigin[i];
}
return resultGeometry;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::
refineGeometry(const InputImageType * input, const RegistrationType * registration,
bool throwOnError)
{
mitk::ImageMappingHelper::ResultImageType::Pointer result = nullptr;
if (!registration)
{
mitkThrow() << "Cannot refine image geometry. Passed registration pointer is nullptr.";
}
if (!input)
{
mitkThrow() << "Cannot refine image geometry. Passed image pointer is nullptr.";
}
mitk::MITKRegistrationHelper::Affine3DTransformType::Pointer spTransform = mitk::MITKRegistrationHelper::getAffineMatrix(registration, false);
if (spTransform.IsNull() && throwOnError)
{
mitkThrow() << "Cannot refine image geometry. Registration does not contain a suitable direct mapping kernel (3D affine transformation or compatible required).";
}
if (spTransform.IsNotNull())
{
//copy input image
result = input->Clone();
//refine geometries
for (unsigned int i = 0; i < result->GetTimeSteps(); ++i)
{ //refine every time step
result->GetGeometry(i)->Compose(spTransform);
}
result->GetTimeGeometry()->Update();
}
return result;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::
refineGeometry(const InputImageType* input, const MITKRegistrationType* registration,
bool throwOnError)
{
if (!registration)
{
mitkThrow() << "Cannot refine image geometry. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot refine image geometry. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot refine image geometry. Passed registration wrapper contains no registration.";
}
if (!input)
{
mitkThrow() << "Cannot refine image geometry. Passed image pointer is nullptr.";
}
ResultImageType::Pointer result = refineGeometry(input, registration->GetRegistration(), throwOnError);
return result;
}
bool
mitk::ImageMappingHelper::
canRefineGeometry(const RegistrationType* registration)
{
bool result = true;
if (!registration)
{
mitkThrow() << "Cannot check refine capability of registration. Passed registration pointer is nullptr.";
}
//if the helper does not return null, we can refine the geometry.
result = mitk::MITKRegistrationHelper::getAffineMatrix(registration,false).IsNotNull();
return result;
}
bool
mitk::ImageMappingHelper::
canRefineGeometry(const MITKRegistrationType* registration)
{
if (!registration)
{
mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper contains no registration.";
}
return canRefineGeometry(registration->GetRegistration());
}
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp
index 0b11df2dad..d6a022136a 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp
@@ -1,407 +1,407 @@
/*============================================================================
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 "mitkMAPAlgorithmHelper.h"
//itk
#include <itkImageDuplicator.h>
// Mitk
#include <mitkImageAccessByItk.h>
#include <mitkPointSetMappingHelper.h>
// MatchPoint
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
#include <mapPointSetRegistrationAlgorithmInterface.h>
#include <mapDummyImageRegistrationAlgorithm.h>
#include <mapAlgorithmIdentificationInterface.h>
namespace mitk
{
MAPAlgorithmHelper::MAPAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm)
: m_AlgorithmBase(algorithm), m_Error(CheckError::none)
{
m_AllowImageCasting = true;
}
bool MAPAlgorithmHelper::HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm)
{
using InternalDefault2DImageType = itk::Image<map::core::discrete::InternalPixelType, 2>;
using InternalDefault3DImageType = itk::Image<map::core::discrete::InternalPixelType, 3>;
using Alg2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault2DImageType, InternalDefault2DImageType>;
if (dynamic_cast<Alg2DType*>(algorithm) != nullptr) return true;
using Alg3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault3DImageType, InternalDefault3DImageType>;
if (dynamic_cast<Alg3DType*>(algorithm) != nullptr) return true;
using Alg2D3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault2DImageType, InternalDefault3DImageType>;
if (dynamic_cast<Alg2D3DType*>(algorithm) != nullptr) return true;
using Alg3D2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault3DImageType, InternalDefault2DImageType>;
if (dynamic_cast<Alg3D2DType*>(algorithm) != nullptr) return true;
return false;
}
bool MAPAlgorithmHelper::HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm)
{
typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType;
typedef const ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface<InternalDefaultPointSetType, InternalDefaultPointSetType>
PointSetRegInterface;
return dynamic_cast<PointSetRegInterface*>(algorithm) != nullptr;
}
map::core::RegistrationBase::Pointer
MAPAlgorithmHelper::
GetRegistration() const
{
map::core::RegistrationBase::Pointer spResult;
unsigned int movingDim = m_AlgorithmBase->getMovingDimensions();
unsigned int targetDim = m_AlgorithmBase->getTargetDimensions();
if (movingDim != targetDim)
{
mapDefaultExceptionStaticMacro( <<
"Error, algorithm instance has unequal dimensionality and is therefore not supported in the current version of MAPAlgorithmHelper.");
}
if (movingDim > 3)
{
mapDefaultExceptionStaticMacro( <<
"Error, algorithm instance has a dimensionality larger than 3 and is therefore not supported in the current version of MAPAlgorithmHelper.");
}
typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<2, 2> RegistrationAlg2D2DInterface;
typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<3, 3> RegistrationAlg3D3DInterface;
RegistrationAlg2D2DInterface* pRegAlgorithm2D2D = dynamic_cast<RegistrationAlg2D2DInterface*>
(m_AlgorithmBase.GetPointer());
RegistrationAlg3D3DInterface* pRegAlgorithm3D3D = dynamic_cast<RegistrationAlg3D3DInterface*>
(m_AlgorithmBase.GetPointer());
if (pRegAlgorithm2D2D)
{
spResult = pRegAlgorithm2D2D->getRegistration();
}
if (pRegAlgorithm3D3D)
{
spResult = pRegAlgorithm3D3D->getRegistration();
}
return spResult;
}
mitk::MAPRegistrationWrapper::Pointer
MAPAlgorithmHelper::
GetMITKRegistrationWrapper() const
{
map::core::RegistrationBase::Pointer spInternalResult = GetRegistration();
mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(spInternalResult);
return spResult;
}
static const mitk::Image* GetDataAsImage(const mitk::BaseData* data)
{
return dynamic_cast<const mitk::Image*>(data);
}
static const mitk::PointSet* GetDataAsPointSet(const mitk::BaseData* data)
{
return dynamic_cast<const mitk::PointSet*>(data);
}
bool
MAPAlgorithmHelper::
CheckData(const mitk::BaseData* moving, const mitk::BaseData* target, CheckError::Type& error) const
{
if (! m_AlgorithmBase)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined.");
}
if (! moving)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr.");
}
if (! target)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr.");
}
bool result = false;
m_Error = CheckError::unsupportedDataType;
unsigned int movingDim = m_AlgorithmBase->getMovingDimensions();
unsigned int targetDim = m_AlgorithmBase->getTargetDimensions();
if (movingDim != targetDim)
{
m_Error = CheckError::wrongDimension;
}
else
{
//First check if data are point sets or images
if (GetDataAsPointSet(target) && GetDataAsPointSet(moving))
{
typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType;
typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface<InternalDefaultPointSetType, InternalDefaultPointSetType>
PointSetRegInterface;
PointSetRegInterface* pPointSetInterface = dynamic_cast<PointSetRegInterface*>
(m_AlgorithmBase.GetPointer());
if (!pPointSetInterface)
{
result = false;
m_Error = CheckError::unsupportedDataType;
}
}
else if (GetDataAsImage(moving) && GetDataAsImage(target))
{
if (movingDim == 2)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages,
2);
}
else if (movingDim == 3)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages,
3);
}
else
{
m_Error = CheckError::wrongDimension;
}
if (m_Error == CheckError::none || (m_AllowImageCasting && m_Error == CheckError::onlyByCasting))
{
result = true;
}
}
}
error = m_Error;
return result;
}
void MAPAlgorithmHelper::SetAllowImageCasting(bool allowCasting)
{
this->m_AllowImageCasting = allowCasting;
}
bool MAPAlgorithmHelper::GetAllowImageCasting() const
{
return this->m_AllowImageCasting;
}
void MAPAlgorithmHelper::SetData(const mitk::BaseData* moving, const mitk::BaseData* target)
{
if (! m_AlgorithmBase)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined.");
}
if (! moving)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr.");
}
if (! target)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr.");
}
unsigned int movingDim = m_AlgorithmBase->getMovingDimensions();
unsigned int targetDim = m_AlgorithmBase->getTargetDimensions();
if (movingDim != targetDim)
{
mapDefaultExceptionStaticMacro( <<
"Error, cannot set data. Current version of MAPAlgorithmHelper only supports images/point sets with same dimensionality.");
}
if (GetDataAsPointSet(target) && GetDataAsPointSet(moving))
{
typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType;
typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface<InternalDefaultPointSetType, InternalDefaultPointSetType>
PointSetRegInterface;
PointSetRegInterface* pPointSetInterface = dynamic_cast<PointSetRegInterface*>
(m_AlgorithmBase.GetPointer());
pPointSetInterface->setMovingPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP(
GetDataAsPointSet(moving)->GetPointSet()));
pPointSetInterface->setTargetPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP(
GetDataAsPointSet(target)->GetPointSet()));
}
else if (GetDataAsImage(moving) && GetDataAsImage(target))
{
if (movingDim == 2)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 2);
}
else if (movingDim == 3)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 3);
}
}
}
template<typename TInImageType, typename TOutImageType>
typename TOutImageType::Pointer MAPAlgorithmHelper::CastImage(const TInImageType* input) const
{
typedef itk::CastImageFilter< TInImageType, TOutImageType > CastFilterType;
typename CastFilterType::Pointer spImageCaster = CastFilterType::New();
spImageCaster->SetInput(input);
typename TOutImageType::Pointer spImage = spImageCaster->GetOutput();
spImageCaster->Update();
return spImage;
}
template<typename TPixelType1, unsigned int VImageDimension1,
typename TPixelType2, unsigned int VImageDimension2>
void MAPAlgorithmHelper::DoSetImages(const itk::Image<TPixelType1, VImageDimension1>* moving,
const itk::Image<TPixelType2, VImageDimension2>* target)
{
typedef itk::Image<TPixelType1, VImageDimension1> MovingImageType;
typedef itk::Image<TPixelType2, VImageDimension2> TargetImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension1>
InternalDefaultMovingImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension2>
InternalDefaultTargetImageType;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<MovingImageType, TargetImageType>
ImageRegInterface;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefaultMovingImageType, InternalDefaultTargetImageType>
DefaultImageRegInterface;
ImageRegInterface* pImageInterface = dynamic_cast<ImageRegInterface*>(m_AlgorithmBase.GetPointer());
DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast<DefaultImageRegInterface*>
(m_AlgorithmBase.GetPointer());
if (pImageInterface)
{
//just set directly and you are done
- /**@todo the duplication work arround is needed due to a insufficuence
+ /**@todo the duplication work around is needed due to a insufficuence
in the AccessTwoImagesFixedDimensionByItk macro. The macro always cast
the passed image into non const (even if tha image was passed as const).
This behavior enforces the unnecessary use of an writeaccessor, which as a consequence
will lead to redundant access exceptions as long as the algorithm exists;
e.g. in the typical scenario with the MatchPoint Plugins*/
typedef itk::ImageDuplicator< MovingImageType > MovingDuplicatorType;
typedef itk::ImageDuplicator< TargetImageType > TargetDuplicatorType;
typename MovingDuplicatorType::Pointer mDuplicator = MovingDuplicatorType::New();
mDuplicator->SetInputImage(moving);
mDuplicator->Update();
typename TargetDuplicatorType::Pointer tDuplicator = TargetDuplicatorType::New();
tDuplicator->SetInputImage(target);
tDuplicator->Update();
typename MovingImageType::Pointer clonedMoving = mDuplicator->GetOutput();
typename TargetImageType::Pointer clonedTarget = tDuplicator->GetOutput();
pImageInterface->setTargetImage(clonedTarget);
pImageInterface->setMovingImage(clonedMoving);
}
else if (pDefaultImageInterface)
{
//you may convert it to the default image type and use it then
if (! m_AllowImageCasting)
{
mapDefaultExceptionStaticMacro( <<
"Error, cannot set images. MAPAlgorithmHelper has to convert them into MatchPoint default images, but is not allowed. Please reconfigure helper.");
}
typename InternalDefaultTargetImageType::Pointer spCastedTarget =
CastImage<TargetImageType, InternalDefaultTargetImageType>(target);
typename InternalDefaultMovingImageType::Pointer spCastedMoving =
CastImage<MovingImageType, InternalDefaultMovingImageType>(moving);
pDefaultImageInterface->setTargetImage(spCastedTarget);
pDefaultImageInterface->setMovingImage(spCastedMoving);
}
else
{
mapDefaultExceptionStaticMacro( << "Error, algorithm is not able to use the based images.");
}
}
template<typename TPixelType1, unsigned int VImageDimension1,
typename TPixelType2, unsigned int VImageDimension2>
void MAPAlgorithmHelper::DoCheckImages(const itk::Image<TPixelType1, VImageDimension1>* /*moving*/,
const itk::Image<TPixelType2, VImageDimension2>* /*target*/) const
{
typedef itk::Image<TPixelType1, VImageDimension1> MovingImageType;
typedef itk::Image<TPixelType2, VImageDimension2> TargetImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension1>
InternalDefaultMovingImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension2>
InternalDefaultTargetImageType;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<MovingImageType, TargetImageType>
ImageRegInterface;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefaultMovingImageType, InternalDefaultTargetImageType>
DefaultImageRegInterface;
ImageRegInterface* pImageInterface = dynamic_cast<ImageRegInterface*>(m_AlgorithmBase.GetPointer());
DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast<DefaultImageRegInterface*>
(m_AlgorithmBase.GetPointer());
if (pImageInterface)
{
//just set directly and you are done
m_Error = CheckError::none;
}
else if (pDefaultImageInterface)
{
//you may convert it to the default image type and use it then
m_Error = CheckError::onlyByCasting;
}
else
{
m_Error = CheckError::unsupportedDataType;
}
}
mapGenerateAlgorithmUIDPolicyMacro(DummyRegIDPolicy, "de.dkfz.dipp", "Identity", "1.0.0", "");
mitk::MAPRegistrationWrapper::Pointer GenerateIdentityRegistration3D()
{
typedef map::algorithm::DummyImageRegistrationAlgorithm<map::core::discrete::Elements<3>::InternalImageType, map::core::discrete::Elements<3>::InternalImageType, DummyRegIDPolicy>
DummyRegType;
DummyRegType::Pointer regAlg = DummyRegType::New();
mitk::MAPAlgorithmHelper helper(regAlg);
map::core::discrete::Elements<3>::InternalImageType::Pointer dummyImg =
map::core::discrete::Elements<3>::InternalImageType::New();
dummyImg->Allocate();
regAlg->setTargetImage(dummyImg);
regAlg->setMovingImage(dummyImg);
auto dummyReg = mitk::MAPRegistrationWrapper::New(regAlg->getRegistration());
return dummyReg;
}
}
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp
index c71d4bc2ff..5f33e4740a 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp
@@ -1,144 +1,144 @@
/*============================================================================
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 "mapRegistration.h"
#include "mitkPointSetMappingHelper.h"
#include "mitkRegistrationHelper.h"
#include "mapPointSetMappingTask.h"
::map::core::continuous::Elements<3>::InternalPointSetType::Pointer mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP(const mitk::PointSet::DataType* mitkSet)
{
if (! mitkSet) mapDefaultExceptionStaticMacro(<< "Error, cannot convert point set. Passed mitk point set is null.");
::map::core::continuous::Elements<3>::InternalPointSetType::Pointer mapSet = ::map::core::continuous::Elements<3>::InternalPointSetType::New();
::map::core::continuous::Elements<3>::InternalPointSetType::PointsContainer::Pointer mapContainer = ::map::core::continuous::Elements<3>::InternalPointSetType::PointsContainer::New();
::map::core::continuous::Elements<3>::InternalPointSetType::PointDataContainer::Pointer mapDataContainer = ::map::core::continuous::Elements<3>::InternalPointSetType::PointDataContainer::New();
mapSet->SetPoints(mapContainer);
mapSet->SetPointData(mapDataContainer);
unsigned int pointCount = mitkSet->GetNumberOfPoints();
for (unsigned int pointId = 0; pointId < pointCount; ++pointId)
{
mapSet->SetPoint(pointId, mitkSet->GetPoint(pointId));
mitk::PointSet::PointDataType data;
if (mitkSet->GetPointData(pointId,&data))
{
mapSet->SetPointData(pointId,data.id);
}
}
return mapSet;
}
::mitk::PointSet::Pointer
mitk::PointSetMappingHelper::map(const ::mitk::PointSet* input, const mitk::PointSetMappingHelper::RegistrationType* registration, int timeStep,
bool throwOnMappingError, const ::mitk::PointSet::PointDataType& errorPointValue)
{
if (!registration)
{
mitkThrow() << "Cannot map point set. Passed registration wrapper pointer is nullptr.";
}
if (!input)
{
mitkThrow() << "Cannot map point set. Passed point set pointer is nullptr.";
}
if (static_cast<int>(input->GetTimeSteps())<=timeStep && timeStep>=0)
{
mitkThrow() << "Cannot set point set. Selected time step is larger then mitk point set. MITK time step count: "<<input->GetTimeSteps()<<"; selected time step: "<<timeStep;
}
::mitk::PointSet::Pointer result = input->Clone();
typedef ::map::core::continuous::Elements<3>::InternalPointSetType MAPPointSetType;
typedef ::map::core::Registration<3,3> ConcreteRegistrationType;
const ConcreteRegistrationType* castedReg = dynamic_cast<const ConcreteRegistrationType*>(registration);
if (!castedReg)
{
mitkThrow() <<"Moving and/or fixed dimension of the registration is not 3. Cannot map point 3D set.";
}
typedef ::map::core::PointSetMappingTask<ConcreteRegistrationType, MAPPointSetType, MAPPointSetType> MappingTaskType;
MappingTaskType::ErrorPointValueType internalErrorValue = itk::NumericTraits<MappingTaskType::ErrorPointValueType>::NonpositiveMin();
MappingTaskType::Pointer spTask = MappingTaskType::New();
spTask->setRegistration(castedReg);
spTask->setThrowOnMappingError(throwOnMappingError);
spTask->setErrorPointValue(internalErrorValue);
unsigned int timePos = timeStep;
unsigned int timeEndPos = timeStep+1;
if (timeStep < 0)
{
timePos = 0;
timeEndPos = input->GetTimeSteps();
}
while (timePos<timeEndPos)
{
MAPPointSetType::Pointer inputTempSet = ConvertPointSetMITKtoMAP(input->GetPointSet(timePos));
spTask->setInputPointSet(inputTempSet);
spTask->execute();
MAPPointSetType::Pointer mappedSet = spTask->getResultPointSet();
unsigned int pointCount = input->GetSize(timePos);
for (unsigned int pointId = 0; pointId < pointCount; ++pointId)
{
result->SetPoint(pointId, mappedSet->GetPoint(pointId), timePos);
bool invalid = true;
MAPPointSetType::PixelType mappedData;
if (mappedSet->GetPointData(pointId,&mappedData))
{
invalid = mappedData == internalErrorValue;
}
if (invalid)
{
result->GetPointSet(timePos)->GetPointData()->SetElement(pointId,errorPointValue);
}
else
{
result->GetPointSet(timePos)->GetPointData()->SetElement(pointId,input->GetPointSet(timePos)->GetPointData()->GetElement(pointId));
}
}
++timePos;
}
return result;
}
::mitk::PointSet::Pointer
mitk::PointSetMappingHelper::map(const ::mitk::PointSet* input, const MITKRegistrationType* registration, int timeStep,
bool throwOnMappingError, const ::mitk::PointSet::PointDataType& errorPointValue)
{
if (!registration)
{
mitkThrow() << "Cannot map point set. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot map point set. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot map point set. Passed registration wrapper contains no registration.";
}
if (!input)
{
mitkThrow() << "Cannot map point set. Passed point set pointer is nullptr.";
}
::mitk::PointSet::Pointer result = map(input, registration->GetRegistration(), timeStep, throwOnMappingError, errorPointValue);
return result;
}
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp
index 5950e7ba52..4b260cc338 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp
@@ -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.
============================================================================*/
#include "itkCommand.h"
#include "mitkTimeFramesRegistrationHelper.h"
#include <mitkImageTimeSelector.h>
#include <mitkImageReadAccessor.h>
#include <mitkMaskedAlgorithmHelper.h>
#include <mitkMAPAlgorithmHelper.h>
mitk::Image::Pointer
mitk::TimeFramesRegistrationHelper::GetFrameImage(const mitk::Image* image,
mitk::TimePointType timePoint) const
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(image);
imageTimeSelector->SetTimeNr(timePoint);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::Image::Pointer frameImage = imageTimeSelector->GetOutput();
return frameImage;
};
void
mitk::TimeFramesRegistrationHelper::Generate()
{
CheckValidInputs();
//prepare processing
mitk::Image::Pointer targetFrame = GetFrameImage(this->m_4DImage, 0);
this->m_Registered4DImage = this->m_4DImage->Clone();
Image::ConstPointer mask;
if (m_TargetMask.IsNotNull())
{
if (m_TargetMask->GetTimeSteps() > 1)
{
mask = GetFrameImage(m_TargetMask, 0);
}
else
{
mask = m_TargetMask;
}
}
double progressDelta = 1.0 / ((this->m_4DImage->GetTimeSteps() - 1) * 3.0);
m_Progress = 0.0;
//process the frames
for (unsigned int i = 1; i < this->m_4DImage->GetTimeSteps(); ++i)
{
Image::Pointer movingFrame = GetFrameImage(this->m_4DImage, i);
Image::Pointer mappedFrame;
IgnoreListType::iterator finding = std::find(m_IgnoreList.begin(), m_IgnoreList.end(), i);
if (finding == m_IgnoreList.end())
{
//frame should be processed
RegistrationPointer reg = DoFrameRegistration(movingFrame, targetFrame, mask);
m_Progress += progressDelta;
this->InvokeEvent(::mitk::FrameRegistrationEvent(nullptr,
- "Registred frame #" +::map::core::convert::toStr(i)));
+ "Registered frame #" +::map::core::convert::toStr(i)));
mappedFrame = DoFrameMapping(movingFrame, reg, targetFrame);
m_Progress += progressDelta;
this->InvokeEvent(::mitk::FrameMappingEvent(nullptr,
"Mapped frame #" + ::map::core::convert::toStr(i)));
mitk::ImageReadAccessor accessor(mappedFrame, mappedFrame->GetVolumeData(0, 0, nullptr,
mitk::Image::ReferenceMemory));
this->m_Registered4DImage->SetVolume(accessor.GetData(), i);
this->m_Registered4DImage->GetTimeGeometry()->SetTimeStepGeometry(mappedFrame->GetGeometry(), i);
m_Progress += progressDelta;
}
else
{
m_Progress += 3 * progressDelta;
}
this->InvokeEvent(::itk::ProgressEvent());
}
};
mitk::Image::Pointer
mitk::TimeFramesRegistrationHelper::GetRegisteredImage()
{
if (this->HasOutdatedResult())
{
Generate();
}
return m_Registered4DImage;
};
void
mitk::TimeFramesRegistrationHelper::
SetIgnoreList(const IgnoreListType& il)
{
m_IgnoreList = il;
this->Modified();
}
void
mitk::TimeFramesRegistrationHelper::ClearIgnoreList()
{
m_IgnoreList.clear();
this->Modified();
};
mitk::TimeFramesRegistrationHelper::RegistrationPointer
mitk::TimeFramesRegistrationHelper::DoFrameRegistration(const mitk::Image* movingFrame,
const mitk::Image* targetFrame, const mitk::Image* targetMask) const
{
mitk::MAPAlgorithmHelper algHelper(m_Algorithm);
algHelper.SetAllowImageCasting(true);
algHelper.SetData(movingFrame, targetFrame);
if (targetMask)
{
mitk::MaskedAlgorithmHelper maskHelper(m_Algorithm);
maskHelper.SetMasks(nullptr, targetMask);
}
return algHelper.GetRegistration();
};
mitk::Image::Pointer mitk::TimeFramesRegistrationHelper::DoFrameMapping(
const mitk::Image* movingFrame, const RegistrationType* reg, const mitk::Image* targetFrame) const
{
return mitk::ImageMappingHelper::map(movingFrame, reg, !m_AllowUndefPixels, m_PaddingValue,
targetFrame->GetGeometry(), !m_AllowUnregPixels, m_ErrorValue, m_InterpolatorType);
};
bool
mitk::TimeFramesRegistrationHelper::HasOutdatedResult() const
{
if (m_Registered4DImage.IsNull())
{
return true;
}
bool result = false;
if (m_Registered4DImage->GetMTime() > this->GetMTime())
{
result = true;
}
if (m_Algorithm.IsNotNull())
{
if (m_Algorithm->GetMTime() > this->GetMTime())
{
result = true;
}
}
if (m_4DImage.IsNotNull())
{
if (m_4DImage->GetMTime() > this->GetMTime())
{
result = true;
}
}
if (m_TargetMask.IsNotNull())
{
if (m_TargetMask->GetMTime() > this->GetMTime())
{
result = true;
}
}
return result;
};
void
mitk::TimeFramesRegistrationHelper::CheckValidInputs() const
{
if (m_4DImage.IsNull())
{
mitkThrow() << "Cannot register image. Input 4D image is not set.";
}
if (m_Algorithm.IsNull())
{
mitkThrow() << "Cannot register image. Algorithm is not set.";
}
if (m_4DImage->GetTimeSteps() <= 1)
{
mitkThrow() << "Cannot register image. Input 4D image must have 2 or more time steps.";
}
for (IgnoreListType::const_iterator pos = this->m_IgnoreList.begin();
pos != this->m_IgnoreList.end(); ++pos)
{
if (*pos >= m_4DImage->GetTimeSteps())
{
mitkThrow() <<
- "Cannot register image. Ignore list containes at least one inexistant frame. Invalid frame index: "
+ "Cannot register image. Ignore list contains at least one inexistant frame. Invalid frame index: "
<< *pos;
}
}
};
double
mitk::TimeFramesRegistrationHelper::GetProgress() const
{
return m_Progress;
};
diff --git a/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp b/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp
index fafaf04fb8..fbad35068c 100644
--- a/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp
+++ b/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp
@@ -1,836 +1,836 @@
/*============================================================================
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 <mitkAbstractTransformGeometry.h>
#include <mitkDataNode.h>
#include <mitkImageSliceSelector.h>
#include <mitkLevelWindowProperty.h>
#include <mitkLookupTableProperty.h>
#include <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkResliceMethodProperty.h>
#include <mitkVtkResliceInterpolationProperty.h>
#include <mitkPixelType.h>
#include <mitkTransferFunctionProperty.h>
#include "mitkImageStatisticsHolder.h"
#include "mitkPlaneClipping.h"
#include "mitkRegVisPropertyTags.h"
#include "mitkRegVisHelper.h"
#include "mitkRegEvalStyleProperty.h"
#include "mitkRegEvalWipeStyleProperty.h"
//MITK Rendering
#include "mitkRegEvaluationMapper2D.h"
#include "vtkMitkThickSlicesFilter.h"
#include "vtkMitkLevelWindowFilter.h"
#include "vtkNeverTranslucentTexture.h"
//VTK
#include <vtkProperty.h>
#include <vtkTransform.h>
#include <vtkMatrix4x4.h>
#include <vtkLookupTable.h>
#include <vtkImageData.h>
#include <vtkPoints.h>
#include <vtkGeneralTransform.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageReslice.h>
#include <vtkImageChangeInformation.h>
#include <vtkPlaneSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkCellArray.h>
#include <vtkCamera.h>
#include <vtkColorTransferFunction.h>
#include <vtkImageCheckerboard.h>
#include <vtkImageWeightedSum.h>
#include <vtkImageMathematics.h>
#include <vtkImageRectilinearWipe.h>
#include <vtkImageGradientMagnitude.h>
#include <vtkImageAppendComponents.h>
#include <vtkImageExtractComponents.h>
//ITK
#include <itkRGBAPixel.h>
#include <mitkRenderingModeProperty.h>
//MatchPoint
#include <mitkRegEvaluationObject.h>
#include <mitkImageMappingHelper.h>
mitk::RegEvaluationMapper2D::RegEvaluationMapper2D()
{
}
mitk::RegEvaluationMapper2D::~RegEvaluationMapper2D()
{
}
//set the two points defining the textured plane according to the dimension and spacing
void mitk::RegEvaluationMapper2D::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 sagittal) 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::RegEvaluationMapper2D::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::RegEvaluationMapper2D::GetTargetImage( void )
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() );
if (evalObj)
{
return evalObj->GetTargetImage();
}
return nullptr;
}
const mitk::Image* mitk::RegEvaluationMapper2D::GetMovingImage( void )
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() );
if (evalObj)
{
return evalObj->GetMovingImage();
}
return nullptr;
}
const mitk::DataNode* mitk::RegEvaluationMapper2D::GetTargetNode(void)
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData());
if (evalObj)
{
return evalObj->GetTargetNode();
}
return nullptr;
}
const mitk::DataNode* mitk::RegEvaluationMapper2D::GetMovingNode(void)
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData());
if (evalObj)
{
return evalObj->GetMovingNode();
}
return nullptr;
}
const mitk::MAPRegistrationWrapper* mitk::RegEvaluationMapper2D::GetRegistration( void )
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() );
if (evalObj)
{
return evalObj->GetRegistration();
}
return nullptr;
}
vtkProp* mitk::RegEvaluationMapper2D::GetVtkProp(mitk::BaseRenderer* renderer)
{
//return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
void mitk::RegEvaluationMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer )
{
bool updated = false;
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::Image::Pointer targetInput = const_cast< mitk::Image * >( this->GetTargetImage() );
mitk::DataNode* datanode = this->GetDataNode();
if ( targetInput.IsNull() || targetInput->IsInitialized() == false )
{
return;
}
mitk::Image::ConstPointer movingInput = this->GetMovingImage();
if ( movingInput.IsNull() || movingInput->IsInitialized() == false )
{
return;
}
mitk::MAPRegistrationWrapper::ConstPointer reg = this->GetRegistration();
//check if there is a valid worldGeometry
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if( ( worldGeometry == nullptr ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() ))
{
return;
}
if(targetInput->GetMTime()>localStorage->m_LastUpdateTime
|| (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified?
|| (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()))
{ //target input has been modified -> reslice target input
targetInput->Update();
// 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, targetInput->GetSlicedGeometry() ) )
{
// 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_EvaluationImage = nullptr;
localStorage->m_Mapper->SetInputData( localStorage->m_EmptyPolyData );
return;
}
//set main input for ExtractSliceFilter
localStorage->m_Reslicer->SetInput(targetInput);
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( targetInput->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 ( (targetInput->GetDimension() >= 3) && (targetInput->GetDimension(2) > 1) )
{
VtkResliceInterpolationProperty *resliceInterpolationProperty;
datanode->GetProperty(
resliceInterpolationProperty, "reslice interpolation" );
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);
}
- //this is needed when thick mode was enable bevore. These variable have to be reset to default values
+ //this is needed when thick mode was enable before. 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_slicedTargetImage = localStorage->m_Reslicer->GetOutput();
updated = true;
}
if(updated ||
movingInput->GetMTime() > localStorage->m_LastUpdateTime ||
reg->GetMTime() > localStorage->m_LastUpdateTime)
{
//Map moving image
localStorage->m_slicedMappedImage = mitk::ImageMappingHelper::map(movingInput,reg,false,0,localStorage->m_slicedTargetImage->GetGeometry(),false,0);
updated = true;
}
// Bounds information for reslicing (only required if reference geometry
// is present)
//this used for generating a vtkPLaneSource with the right size
double sliceBounds[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
if (updated
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime()))
{
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] = { 0.0, 0.0, 0.0, 0.0, 0.0, 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.
const PlaneGeometry *planeGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry);
mitk::PlaneClipping::CalculateClippedPlaneBounds(targetInput->GetGeometry(), planeGeometry, textureClippingBounds);
textureClippingBounds[0] = static_cast<int>(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[1] = static_cast<int>(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[2] = static_cast<int>(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5);
textureClippingBounds[3] = static_cast<int>(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5);
//clipping bounds for cutting the image
localStorage->m_TargetLevelWindowFilter->SetClippingBounds(textureClippingBounds);
localStorage->m_MappedLevelWindowFilter->SetClippingBounds(textureClippingBounds);
}
this->ApplyLookuptable(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter);
this->ApplyLookuptable(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter);
this->ApplyLevelWindow(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter);
this->ApplyLevelWindow(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter);
//connect the input with the levelwindow filter
localStorage->m_TargetLevelWindowFilter->SetInputData(localStorage->m_slicedTargetImage->GetVtkImageData());
localStorage->m_MappedLevelWindowFilter->SetInputData(localStorage->m_slicedMappedImage->GetVtkImageData());
localStorage->m_TargetExtractFilter->SetInputConnection(localStorage->m_TargetLevelWindowFilter->GetOutputPort());
localStorage->m_MappedExtractFilter->SetInputConnection(localStorage->m_MappedLevelWindowFilter->GetOutputPort());
localStorage->m_TargetExtractFilter->SetComponents(0);
localStorage->m_MappedExtractFilter->SetComponents(0);
updated = true;
}
//Generate evaluation image
bool isStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalStyle,localStorage->m_LastUpdateTime);
bool isBlendOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalBlendFactor,localStorage->m_LastUpdateTime);
bool isCheckerOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalCheckerCount,localStorage->m_LastUpdateTime);
bool isWipeStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalWipeStyle,localStorage->m_LastUpdateTime);
bool isContourOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalTargetContour,localStorage->m_LastUpdateTime);
bool isPositionOutdated = mitk::PropertyIsOutdated(datanode, mitk::nodeProp_RegEvalCurrentPosition, localStorage->m_LastUpdateTime);
if (updated ||
isStyleOutdated ||
isBlendOutdated ||
isCheckerOutdated ||
isWipeStyleOutdated ||
isContourOutdated ||
isPositionOutdated)
{
mitk::RegEvalStyleProperty::Pointer evalStyleProp = mitk::RegEvalStyleProperty::New();
datanode->GetProperty(evalStyleProp, mitk::nodeProp_RegEvalStyle);
switch (evalStyleProp->GetValueAsId())
{
case 0 :
{
PrepareBlend(datanode, localStorage);
break;
}
case 1 :
{
PrepareColorBlend(localStorage);
break;
}
case 2 :
{
PrepareCheckerBoard(datanode, localStorage);
break;
}
case 3 :
{
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
Point3D currentPos3D;
datanode->GetPropertyValue<Point3D>(mitk::nodeProp_RegEvalCurrentPosition, currentPos3D);
Point2D currentPos2D;
worldGeometry->Map(currentPos3D, currentPos2D);
Point2D currentIndex2D;
worldGeometry->WorldToIndex(currentPos2D, currentIndex2D);
PrepareWipe(datanode, localStorage, currentIndex2D);
break;
}
case 4 :
{
PrepareDifference(localStorage);
break;
}
case 5 :
{
PrepareContour(datanode, localStorage);
break;
}
}
updated = true;
}
if(updated
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime()) )
{
this->ApplyOpacity( renderer );
// do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter)
localStorage->m_Texture->SetColorModeToDirectScalars();
// 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->SetInputData(localStorage->m_EvaluationImage);
this->TransformActor( renderer );
vtkActor* contourShadowActor = dynamic_cast<vtkActor*> (localStorage->m_Actors->GetParts()->GetItemAsObject(0));
//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_Actor->SetTexture(localStorage->m_Texture);
contourShadowActor->SetVisibility( false );
// We have been modified => save this for next Update()
localStorage->m_LastUpdateTime.Modified();
}
}
void mitk::RegEvaluationMapper2D::PrepareContour( mitk::DataNode* datanode, LocalStorage * localStorage )
{
bool targetContour = true;
datanode->GetBoolProperty(mitk::nodeProp_RegEvalTargetContour,targetContour);
vtkSmartPointer<vtkImageGradientMagnitude> magFilter =
vtkSmartPointer<vtkImageGradientMagnitude>::New();
if(targetContour)
{
magFilter->SetInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
}
else
{
magFilter->SetInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
}
vtkSmartPointer<vtkImageAppendComponents> appendFilter =
vtkSmartPointer<vtkImageAppendComponents>::New();
appendFilter->AddInputConnection(magFilter->GetOutputPort());
appendFilter->AddInputConnection(magFilter->GetOutputPort());
if(targetContour)
{
appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
}
else
{
appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
}
appendFilter->Update();
localStorage->m_EvaluationImage = appendFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareDifference( LocalStorage * localStorage )
{
vtkSmartPointer<vtkImageMathematics> diffFilter =
vtkSmartPointer<vtkImageMathematics>::New();
vtkSmartPointer<vtkImageMathematics> minFilter =
vtkSmartPointer<vtkImageMathematics>::New();
vtkSmartPointer<vtkImageMathematics> maxFilter =
vtkSmartPointer<vtkImageMathematics>::New();
minFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort());
minFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort());
minFilter->SetOperationToMin();
maxFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort());
maxFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort());
maxFilter->SetOperationToMax();
diffFilter->SetInputConnection(0, maxFilter->GetOutputPort());
diffFilter->SetInputConnection(1, minFilter->GetOutputPort());
diffFilter->SetOperationToSubtract();
diffFilter->Update();
localStorage->m_EvaluationImage = diffFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareWipe(mitk::DataNode* datanode, LocalStorage * localStorage, const Point2D& currentIndex2D)
{
mitk::RegEvalWipeStyleProperty::Pointer evalWipeStyleProp = mitk::RegEvalWipeStyleProperty::New();
datanode->GetProperty(evalWipeStyleProp, mitk::nodeProp_RegEvalWipeStyle);
vtkSmartPointer<vtkImageRectilinearWipe> wipedFilter =
vtkSmartPointer<vtkImageRectilinearWipe>::New();
wipedFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort());
wipedFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort());
wipedFilter->SetPosition(currentIndex2D[0], currentIndex2D[1]);
if (evalWipeStyleProp->GetValueAsId() == 0)
{
wipedFilter->SetWipeToQuad();
}
else if (evalWipeStyleProp->GetValueAsId() == 1)
{
wipedFilter->SetWipeToHorizontal();
}
else if (evalWipeStyleProp->GetValueAsId() == 2)
{
wipedFilter->SetWipeToVertical();
}
wipedFilter->Update();
localStorage->m_EvaluationImage = wipedFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareCheckerBoard( mitk::DataNode* datanode, LocalStorage * localStorage )
{
int checkerCount = 5;
datanode->GetIntProperty(mitk::nodeProp_RegEvalCheckerCount,checkerCount);
vtkSmartPointer<vtkImageCheckerboard> checkerboardFilter =
vtkSmartPointer<vtkImageCheckerboard>::New();
checkerboardFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort());
checkerboardFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort());
checkerboardFilter->SetNumberOfDivisions(checkerCount, checkerCount, 1);
checkerboardFilter->Update();
localStorage->m_EvaluationImage = checkerboardFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareColorBlend( LocalStorage * localStorage )
{
vtkSmartPointer<vtkImageAppendComponents> appendFilter =
vtkSmartPointer<vtkImageAppendComponents>::New();
//red channel
appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
//green channel
appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
//blue channel
appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
appendFilter->Update();
localStorage->m_EvaluationImage = appendFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareBlend( mitk::DataNode* datanode, LocalStorage * localStorage )
{
int blendfactor = 50;
datanode->GetIntProperty(mitk::nodeProp_RegEvalBlendFactor,blendfactor);
vtkSmartPointer<vtkImageWeightedSum> blendFilter =
vtkSmartPointer<vtkImageWeightedSum>::New();
blendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
blendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
blendFilter->SetWeight(0, (100 - blendfactor) / 100.);
blendFilter->SetWeight(1,blendfactor/100.);
blendFilter->Update();
localStorage->m_EvaluationImage = blendFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter)
{
LevelWindow levelWindow;
dataNode->GetLevelWindow(levelWindow, renderer, "levelwindow");
levelFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound());
mitk::LevelWindow opacLevelWindow;
if (dataNode->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow"))
{
//pass the opaque level window to the filter
levelFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound());
levelFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound());
}
else
{
//no opaque level window
levelFilter->SetMinOpacity(0.0);
levelFilter->SetMaxOpacity(255.0);
}
}
void mitk::RegEvaluationMapper2D::ApplyLookuptable(mitk::BaseRenderer* renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter)
{
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<mitk::LookupTableProperty*>(dataNode->GetProperty("LookupTable"));
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.
+ //in every iteration of the rendering.
}
levelFilter->SetLookupTable(usedLookupTable);
}
void mitk::RegEvaluationMapper2D::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_Actor->GetProperty()->SetOpacity(opacity);
if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 )
{
dynamic_cast<vtkActor*>( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetOpacity(opacity);
}
}
void mitk::RegEvaluationMapper2D::Update(mitk::BaseRenderer* renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if ( !visible )
{
return;
}
mitk::Image* data = const_cast<mitk::Image *>( this->GetTargetImage() );
if ( data == nullptr )
{
return;
}
// Calculate time step of the input data for the specified renderer (integer value)
this->CalculateTimeStep( renderer );
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry();
if ( ( dataTimeGeometry == nullptr )
|| ( dataTimeGeometry->CountTimeSteps() == 0 )
|| ( !dataTimeGeometry->IsValidTimeStep( this->GetTimestep() ) ) )
{
return;
}
const DataNode *node = this->GetDataNode();
data->UpdateOutputInformation();
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
//check if something important has changed and we need to rerender
if ( (localStorage->m_LastUpdateTime < node->GetMTime()) //was the node modified?
|| (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified?
|| (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified?
|| (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime())
|| (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime()) //was the target node modified?
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime()) //was the moving node modified?
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList()->GetMTime()) //was a target node property modified?
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList(renderer)->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetPropertyList()->GetMTime()) //was a moving node property modified?
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetPropertyList(renderer)->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::RegEvaluationMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite)
{
mitk::RegEvaluationObject* regEval = dynamic_cast<mitk::RegEvaluationObject*>(node->GetData());
if(!regEval)
{
return;
}
// Properties common for both images and segmentations
node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite );
if(regEval->GetTargetImage() && regEval->GetTargetImage()->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 ) ); // set to user configurable default value (see global options)
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();
mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New();
mitkLutProp->SetLookupTable(mitkLut);
node->SetProperty("LookupTable", mitkLutProp);
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);
node->AddProperty(mitk::nodeProp_RegEvalStyle, mitk::RegEvalStyleProperty::New(0), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalBlendFactor, mitk::IntProperty::New(50), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalCheckerCount, mitk::IntProperty::New(3), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalTargetContour, mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalWipeStyle, mitk::RegEvalWipeStyleProperty::New(0), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::Point3dProperty::New(mitk::Point3D()), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
mitk::RegEvaluationMapper2D::LocalStorage* mitk::RegEvaluationMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
void mitk::RegEvaluationMapper2D::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 sagittal
vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_Reslicer->GetResliceAxes();
trans->SetMatrix(matrix);
//transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal)
localStorage->m_Actor->SetUserTransform(trans);
//transform the origin to center based coordinates, because MITK is center based.
localStorage->m_Actor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0);
if ( localStorage->m_Actors->GetNumberOfPaths() > 1 )
{
vtkActor* secondaryActor = dynamic_cast<vtkActor*>( localStorage->m_Actors->GetParts()->GetItemAsObject(0) );
secondaryActor->SetUserTransform(trans);
secondaryActor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0);
}
}
bool mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::RegEvaluationMapper2D::LocalStorage::LocalStorage()
{
m_TargetLevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
m_MappedLevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
m_TargetExtractFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
m_MappedExtractFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
m_mmPerPixel = nullptr;
//Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
//m_Texture = vtkSmartPointer<vtkNeverTranslucentTexture>::New().GetPointer();
m_Texture = vtkSmartPointer<vtkOpenGLTexture>::New().GetPointer();
m_DefaultLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_ColorLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_Reslicer = mitk::ExtractSliceFilter::New();
m_EvaluationImage = vtkSmartPointer<vtkImageData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::New();
mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New();
//built a default lookuptable
mitkLUT->SetType(mitk::LookupTable::GRAYSCALE);
m_DefaultLookupTable = mitkLUT->GetVtkLookupTable();
mitkLUT->SetType(mitk::LookupTable::JET);
m_ColorLookupTable = mitkLUT->GetVtkLookupTable();
//do not repeat the texture (the image)
m_Texture->RepeatOff();
//set the mapper for the actor
m_Actor->SetMapper( m_Mapper );
vtkSmartPointer<vtkActor> outlineShadowActor = vtkSmartPointer<vtkActor>::New();
outlineShadowActor->SetMapper( m_Mapper );
m_Actors->AddPart( outlineShadowActor );
m_Actors->AddPart( m_Actor );
}
diff --git a/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp b/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp
index a41e15fc8f..5fd5fda2f2 100644
--- a/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp
+++ b/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp
@@ -1,278 +1,278 @@
/*============================================================================
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 <vtkPropAssembly.h>
#include <vtkPointData.h>
#include <vtkProperty.h>
#include <vtkCellArray.h>
#include <vtkColorTransferFunction.h>
#include <vtkPolyDataMapper.h>
#include <vtkPolyData.h>
#include <vtkActor.h>
#include <mitkProperties.h>
#include <mitkExceptionMacro.h>
#include <mitkException.h>
#include "mitkMAPRegistrationWrapper.h"
#include "mitkRegistrationWrapperMapperBase.h"
#include "mitkRegVisColorStyleProperty.h"
#include "mitkRegVisHelper.h"
#include "mitkRegVisPropertyTags.h"
mitk::MITKRegistrationWrapperMapperBase::MITKRegistrationWrapperMapperBase()
{
}
mitk::MITKRegistrationWrapperMapperBase::~MITKRegistrationWrapperMapperBase()
{
}
void mitk::MITKRegistrationWrapperMapperBase::GenerateDataForRenderer( mitk::BaseRenderer *renderer )
{
mitk::DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
bool isVisible = true;
node->GetVisibility(isVisible, renderer);
if (!isVisible)
return;
RegWrapperLocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
- //check if updates occured in the node or on the display
+ //check if updates occurred in the node or on the display
bool outdatedRendererGeometry = RendererGeometryIsOutdated(renderer,localStorage->m_LastUpdateTime);
if ( (localStorage->m_LastUpdateTime < node->GetMTime())
|| (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())
|| outdatedRendererGeometry)
{
MITK_DEBUG << "UPDATE NEEDED FOR _ " << renderer->GetName();
bool isGridActive = false;
node->GetBoolProperty(mitk::nodeProp_RegVisGrid,isGridActive);
bool isGlyphActive = false;
node->GetBoolProperty(mitk::nodeProp_RegVisGlyph,isGlyphActive);
bool isPointsActive = false;
node->GetBoolProperty(mitk::nodeProp_RegVisPoints,isPointsActive);
bool showStartGrid = false;
node->GetBoolProperty(mitk::nodeProp_RegVisGridShowStart,showStartGrid);
bool isGridActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGrid,localStorage->m_LastUpdateTime);
bool isGlyphActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGlyph,localStorage->m_LastUpdateTime);
bool isPointsActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisPoints,localStorage->m_LastUpdateTime);
bool showStartGridOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGridShowStart,localStorage->m_LastUpdateTime);
mitk::BaseData::Pointer baseData = node->GetData();
if (baseData.IsNull())
return;
const mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>(baseData.GetPointer());
if (regWrapper == nullptr)
return;
//////////////////////////////////////////////////////////////////////////
//1. Check the FOV and presentation styles
bool outdatedFOV = mitk::GridIsOutdated(node,localStorage->m_LastUpdateTime);
if (outdatedFOV ||isGridActiveOutdated || isGlyphActiveOutdated || isPointsActiveOutdated || outdatedRendererGeometry)
{ // we need to generate the grids/presentation again
const map::core::RegistrationKernelBase<3,3>* regKernel= mitk::GetRelevantRegKernelOfNode(node);
if(!regKernel)
{
mitkThrow() << "No reg kernel for visualization";
}
mitk::BaseGeometry::ConstPointer gridDesc;
unsigned int gridFrequ =5;
if (!GetGeometryDescription(renderer,gridDesc, gridFrequ))
{
return;
};
if(isGridActive)
{
localStorage->m_DeformedGridData = mitk::Generate3DDeformationGrid(gridDesc, gridFrequ, regKernel);
localStorage->m_StartGridData = mitk::Generate3DDeformationGrid(gridDesc,gridFrequ);
localStorage->m_DeformedGridMapper->SetInputData(localStorage->m_DeformedGridData);
localStorage->m_StartGridMapper->SetInputData(localStorage->m_StartGridData);
}
else if (isGlyphActive)
{
localStorage->m_DeformedGridData = mitk::Generate3DDeformationGlyph(gridDesc, regKernel);
localStorage->m_StartGridData = nullptr;
localStorage->m_DeformedGridMapper->SetInputData(localStorage->m_DeformedGridData);
}
else
{
mitkThrow() << "No reg kernel visualization style activated.";
}
}
//////////////////////////////////////////////////////////////////////////
//2.Check if the mappers or actors must be modified
bool isColorStyleOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorStyle,localStorage->m_LastUpdateTime);
bool isColorUniOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorUni,localStorage->m_LastUpdateTime);
bool isColor1Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor1Value,localStorage->m_LastUpdateTime);
bool isColor2Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor2Value,localStorage->m_LastUpdateTime);
bool isColor3Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor3Value,localStorage->m_LastUpdateTime);
bool isColor4Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor4Value,localStorage->m_LastUpdateTime);
bool isColor2MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor2Magnitude,localStorage->m_LastUpdateTime);
bool isColor3MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor3Magnitude,localStorage->m_LastUpdateTime);
bool isColor4MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor4Magnitude,localStorage->m_LastUpdateTime);
bool isColorInterpolateOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorInterpolate,localStorage->m_LastUpdateTime);
if(isColorStyleOutdated || isColorUniOutdated || isColor1Outdated ||
isColor2Outdated || isColor2MagOutdated || isColor3Outdated || isColor3MagOutdated ||
isColor4Outdated || isColor4MagOutdated || isColorInterpolateOutdated)
{
localStorage->m_DeformedGridMapper->ScalarVisibilityOn();
localStorage->m_DeformedGridMapper->SetScalarModeToUsePointData();
localStorage->m_DeformedGridMapper->SelectColorArray( "VectorMagnitude" );
mitk::RegVisColorStyleProperty* colorStyleProp = nullptr;
node->GetProperty(colorStyleProp, mitk::nodeProp_RegVisColorStyle);
float color1[3] = {0.0,0.0,0.0};
node->GetColor( color1, nullptr, mitk::nodeProp_RegVisColor1Value );
float color2[3] = {0.25,0.25,0.25};
node->GetColor( color2, nullptr, mitk::nodeProp_RegVisColor2Value );
float color3[3] = {0.5,0.5,0.5};
node->GetColor( color3, nullptr, mitk::nodeProp_RegVisColor3Value );
float color4[3] = {1.0,1.0,1.0};
node->GetColor( color4, nullptr, mitk::nodeProp_RegVisColor4Value );
double mag2 = 0;
node->GetPropertyValue(mitk::nodeProp_RegVisColor2Magnitude, mag2);
double mag3 = 0;
node->GetPropertyValue(mitk::nodeProp_RegVisColor3Magnitude, mag3);
double mag4 = 0;
node->GetPropertyValue(mitk::nodeProp_RegVisColor4Magnitude, mag4);
bool interpolate = true;
node->GetBoolProperty(mitk::nodeProp_RegVisColorInterpolate,interpolate);
//default :color by vector magnitude
localStorage->m_DeformedGridMapper->SelectColorArray( "VectorMagnitude" );
localStorage->m_DeformedGridMapper->SetUseLookupTableScalarRange(true);
localStorage->m_LUT = vtkSmartPointer<vtkColorTransferFunction>::New();
if (!colorStyleProp || colorStyleProp->GetValueAsId()==0)
{ //uni color mode
float temprgb[3] = {1.0,1.0,1.0};
node->GetColor( temprgb, nullptr, mitk::nodeProp_RegVisColorUni );
localStorage->m_LUT->AddRGBSegment(0.0,temprgb[0],temprgb[1],temprgb[2],1.0,temprgb[0],temprgb[1],temprgb[2]);
localStorage->m_LUT->Build();
localStorage->m_DeformedGridMapper->SetLookupTable(localStorage->m_LUT);
}
else
{
localStorage->m_LUT->AddRGBPoint(0.0,color1[0],color1[1],color1[2]);
localStorage->m_LUT->AddRGBPoint(mag2,color2[0],color2[1],color2[2]);
localStorage->m_LUT->AddRGBPoint(mag3,color3[0],color3[1],color3[2]);
localStorage->m_LUT->AddRGBPoint(mag4,color4[0],color4[1],color4[2]);
if (!interpolate)
{
localStorage->m_LUT->AddRGBPoint(0.99*mag2,color1[0],color1[1],color1[2]);
localStorage->m_LUT->AddRGBPoint(0.99*mag3,color2[0],color2[1],color2[2]);
localStorage->m_LUT->AddRGBPoint(0.99*mag4,color3[0],color3[1],color3[2]);
};
}
localStorage->m_LUT->Build();
localStorage->m_DeformedGridMapper->SetLookupTable(localStorage->m_LUT);
localStorage->m_DeformedGridMapper->Update();
}
bool isGridStartColorOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGridStartColor,localStorage->m_LastUpdateTime);
if(isGridStartColorOutdated)
{
localStorage->m_StartGridMapper->ScalarVisibilityOn();
localStorage->m_StartGridMapper->SetScalarModeToUsePointFieldData();
float temprgb[3];
if (node->GetColor( temprgb, nullptr, mitk::nodeProp_RegVisGridStartColor ))
{
double trgb[3] = { (double) temprgb[0], (double) temprgb[1], (double) temprgb[2] };
localStorage->m_StartGridActor->GetProperty()->SetColor(trgb);
}
}
//////////////////////////////////////////////////////////////////////////
//3. Check if Assembly must be updated
if(isGridActiveOutdated||isGlyphActiveOutdated||isPointsActiveOutdated||showStartGridOutdated)
{
localStorage->m_RegAssembly = vtkSmartPointer<vtkPropAssembly>::New();
if (isGridActive)
{
localStorage->m_RegAssembly->AddPart(localStorage->m_DeformedGridActor);
if (showStartGrid)
{
localStorage->m_RegAssembly->AddPart(localStorage->m_StartGridActor);
}
}
else if (isGlyphActive)
{
localStorage->m_RegAssembly->AddPart(localStorage->m_DeformedGridActor);
}
}
localStorage->m_LastUpdateTime.Modified();
}
}
void mitk::MITKRegistrationWrapperMapperBase::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite)
{
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
vtkProp* mitk::MITKRegistrationWrapperMapperBase::GetVtkProp(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer)->m_RegAssembly;
}
mitk::MITKRegistrationWrapperMapperBase::RegWrapperLocalStorage::RegWrapperLocalStorage()
{
m_DeformedGridActor = vtkSmartPointer<vtkActor>::New();
m_DeformedGridMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_DeformedGridActor->SetMapper(m_DeformedGridMapper);
m_StartGridActor = vtkSmartPointer<vtkActor>::New();
m_StartGridMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_StartGridActor->SetMapper(m_StartGridMapper);
m_RegAssembly = vtkSmartPointer<vtkPropAssembly>::New();
m_LUT = vtkSmartPointer<vtkColorTransferFunction>::New();
m_DeformedGridData = nullptr;
m_StartGridData = nullptr;
}
diff --git a/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp b/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp
index b029d62d78..58cccbbdd7 100644
--- a/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp
+++ b/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp
@@ -1,169 +1,169 @@
/*============================================================================
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 "mitkMAPRegistrationWrapper.h"
#include <mapExceptionObjectMacros.h>
#include <mapRegistrationManipulator.h>
mitk::MAPRegistrationWrapper::MAPRegistrationWrapper(map::core::RegistrationBase* registration) : m_spRegistration(registration)
{
if (registration == nullptr)
{
mitkThrow() << "Error. Cannot create MAPRegistrationWrapper with invalid registration instance (nullptr).";
}
Identifiable::SetUID(registration->getRegistrationUID());
}
mitk::MAPRegistrationWrapper::~MAPRegistrationWrapper()
{
}
void mitk::MAPRegistrationWrapper::SetRequestedRegionToLargestPossibleRegion()
{
//nothing to do
}
bool mitk::MAPRegistrationWrapper::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::MAPRegistrationWrapper::VerifyRequestedRegion()
{
return true;
}
bool mitk::MAPRegistrationWrapper::IsEmptyTimeStep(unsigned int /*t*/) const
{
return m_spRegistration.IsNull();
}
bool mitk::MAPRegistrationWrapper::IsEmpty() const
{
return m_spRegistration.IsNull();
}
void mitk::MAPRegistrationWrapper::SetRequestedRegion(const itk::DataObject*)
{
//nothing to do
}
unsigned int mitk::MAPRegistrationWrapper::GetMovingDimensions() const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return moving dimension. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getMovingDimensions();
}
unsigned int mitk::MAPRegistrationWrapper::GetTargetDimensions() const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return target dimension. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getTargetDimensions();
}
const mitk::MAPRegistrationWrapper::TagMapType& mitk::MAPRegistrationWrapper::GetTags() const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return registration tags. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getTags();
}
bool mitk::MAPRegistrationWrapper::GetTagValue(const TagType & tag, ValueType & value) const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return registration tag value. Wrapper points to invalid registration (nullptr). Tag: " << tag;
}
return m_spRegistration->getTagValue(tag,value);
}
bool mitk::MAPRegistrationWrapper::HasLimitedTargetRepresentation() const
{
if (m_spRegistration.IsNull())
{
- mitkThrow()<< "Error. Cannot determin HasLimitedTargetRepresentation(). Wrapper points to invalid registration (nullptr).";
+ mitkThrow()<< "Error. Cannot determine HasLimitedTargetRepresentation(). Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->hasLimitedTargetRepresentation();
}
bool mitk::MAPRegistrationWrapper::HasLimitedMovingRepresentation() const
{
if (m_spRegistration.IsNull())
{
- mitkThrow()<< "Error. Cannot determin HasLimitedMovingRepresentation(). Wrapper points to invalid registration (nullptr).";
+ mitkThrow()<< "Error. Cannot determine HasLimitedMovingRepresentation(). Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->hasLimitedMovingRepresentation();
}
map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration()
{
return m_spRegistration;
}
const map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration() const
{
return m_spRegistration;
}
void mitk::MAPRegistrationWrapper::PrintSelf (std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os,indent);
if (m_spRegistration.IsNull())
{
os<< "Error. Wrapper points to invalid registration (nullptr).";
}
else
{
os<<std::endl<<indent<<"MatchPoint registration instance:";
m_spRegistration->Print(os,indent.GetNextIndent());
typedef map::core::Registration<3,3> CastedRegType;
const CastedRegType* pCastedReg = dynamic_cast<const CastedRegType*>(m_spRegistration.GetPointer());
os<<std::endl<<indent<<"MatchPoint registration direct kernel instance:";
pCastedReg->getDirectMapping().Print(os,indent.GetNextIndent());
os<<std::endl<<indent<<"MatchPoint registration inverse kernel instance:";
pCastedReg->getInverseMapping().Print(os,indent.GetNextIndent());
}
}
void mitk::MAPRegistrationWrapper::SetUID(const UIDType& uid)
{
if (m_spRegistration.IsNull())
{
mitkThrow() << "Error. Cannot set UID. Wrapper points to invalid registration (nullptr).";
}
Identifiable::SetUID(uid);
::map::core::RegistrationBaseManipulator manip(m_spRegistration);
manip.getTagValues()[::map::tags::RegistrationUID] = uid;
};
mitk::Identifiable::UIDType mitk::MAPRegistrationWrapper::GetUID() const
{
if (m_spRegistration.IsNull())
{
mitkThrow() << "Error. Cannot return UID. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getRegistrationUID();
};
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.cpp
index 1606973326..07b79afc3c 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.cpp
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.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 "QmitkFramesRegistrationJob.h"
// Mitk
#include <mitkImageAccessByItk.h>
// Qt
#include <QThreadPool>
// Map4CTK
#include <mitkImageMappingHelper.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMatchPointPropertyTags.h>
#include <mitkUIDHelper.h>
// MatchPoint
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
const mitk::Image *QmitkFramesRegistrationJob::GetTargetDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spTargetData.GetPointer());
};
const map::algorithm::RegistrationAlgorithmBase *QmitkFramesRegistrationJob::GetLoadedAlgorithm() const
{
return m_spLoadedAlgorithm;
};
void QmitkFramesRegistrationJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event)
{
const map::events::AlgorithmEvent *pAlgEvent = dynamic_cast<const map::events::AlgorithmEvent *>(&event);
const map::events::AlgorithmIterationEvent *pIterationEvent =
dynamic_cast<const map::events::AlgorithmIterationEvent *>(&event);
const map::events::AlgorithmWrapperEvent *pWrapEvent =
dynamic_cast<const map::events::AlgorithmWrapperEvent *>(&event);
const map::events::AlgorithmResolutionLevelEvent *pLevelEvent =
dynamic_cast<const map::events::AlgorithmResolutionLevelEvent *>(&event);
const map::events::InitializingAlgorithmEvent *pInitEvent =
dynamic_cast<const map::events::InitializingAlgorithmEvent *>(&event);
const map::events::StartingAlgorithmEvent *pStartEvent =
dynamic_cast<const map::events::StartingAlgorithmEvent *>(&event);
const map::events::StoppingAlgorithmEvent *pStoppingEvent =
dynamic_cast<const map::events::StoppingAlgorithmEvent *>(&event);
const map::events::StoppedAlgorithmEvent *pStoppedEvent =
dynamic_cast<const map::events::StoppedAlgorithmEvent *>(&event);
const map::events::FinalizingAlgorithmEvent *pFinalizingEvent =
dynamic_cast<const map::events::FinalizingAlgorithmEvent *>(&event);
const map::events::FinalizedAlgorithmEvent *pFinalizedEvent =
dynamic_cast<const map::events::FinalizedAlgorithmEvent *>(&event);
const itk::ProgressEvent *pProgressEvent = dynamic_cast<const itk::ProgressEvent *>(&event);
const mitk::FrameRegistrationEvent *pFrameRegEvent = dynamic_cast<const mitk::FrameRegistrationEvent *>(&event);
const mitk::FrameMappingEvent *pFrameMapEvent = dynamic_cast<const mitk::FrameMappingEvent *>(&event);
if (pProgressEvent)
{
emit FrameProcessed(m_helper->GetProgress());
}
else if (pFrameRegEvent)
{
emit FrameRegistered(m_helper->GetProgress());
}
else if (pFrameMapEvent)
{
emit FrameMapped(m_helper->GetProgress());
}
else if (pInitEvent)
{
emit AlgorithmStatusChanged(QString("Initializing algorithm ..."));
}
else if (pStartEvent)
{
emit AlgorithmStatusChanged(QString("Starting algorithm ..."));
}
else if (pStoppingEvent)
{
emit AlgorithmStatusChanged(QString("Stopping algorithm ..."));
}
else if (pStoppedEvent)
{
emit AlgorithmStatusChanged(QString("Stopped algorithm ..."));
if (!pStoppedEvent->getComment().empty())
{
emit AlgorithmInfo(QString("Stopping condition: ") + QString::fromStdString(pStoppedEvent->getComment()));
}
}
else if (pFinalizingEvent)
{
emit AlgorithmStatusChanged(QString("Finalizing algorithm and results ..."));
}
else if (pFinalizedEvent)
{
emit AlgorithmStatusChanged(QString("Finalized algorithm ..."));
}
else if (pIterationEvent)
{
const IIterativeAlgorithm *pIterative =
dynamic_cast<const IIterativeAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::IterativeAlgorithmInterface::IterationCountType count = 0;
bool hasCount = false;
if (pIterative && pIterative->hasIterationCount())
{
hasCount = true;
count = pIterative->getCurrentIteration();
}
emit AlgorithmIterated(QString::fromStdString(pIterationEvent->getComment()), hasCount, count);
}
else if (pLevelEvent)
{
const IMultiResAlgorithm *pResAlg =
dynamic_cast<const IMultiResAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::MultiResRegistrationAlgorithmInterface::ResolutionLevelCountType count = 0;
bool hasCount = false;
QString info = QString::fromStdString(pLevelEvent->getComment());
if (pResAlg && pResAlg->hasLevelCount())
{
count = pResAlg->getCurrentLevel() + 1;
hasCount = true;
info = QString("Level #") + QString::number(pResAlg->getCurrentLevel() + 1) + QString(" ") + info;
}
emit LevelChanged(info, hasCount, count);
}
else if (pAlgEvent && !pWrapEvent)
{
emit AlgorithmInfo(QString::fromStdString(pAlgEvent->getComment()));
}
}
QmitkFramesRegistrationJob::QmitkFramesRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm)
: m_TargetDataUID("Missing target UID"), m_spLoadedAlgorithm(pAlgorithm)
{
m_MappedName = "Unnamed RegJob";
m_spTargetMask = nullptr;
m_spCommand = ::itk::MemberCommand<QmitkFramesRegistrationJob>::New();
m_spCommand->SetCallbackFunction(this, &QmitkFramesRegistrationJob::OnMapAlgorithmEvent);
m_ObserverID = m_spLoadedAlgorithm->AddObserver(::map::events::AlgorithmEvent(), m_spCommand);
};
QmitkFramesRegistrationJob::~QmitkFramesRegistrationJob()
{
m_spLoadedAlgorithm->RemoveObserver(m_ObserverID);
};
void QmitkFramesRegistrationJob::run()
{
try
{
m_helper = mitk::TimeFramesRegistrationHelper::New();
m_helper->Set4DImage(this->GetTargetDataAsImage());
m_helper->SetTargetMask(this->m_spTargetMask);
m_helper->SetAlgorithm(this->m_spLoadedAlgorithm);
m_helper->SetIgnoreList(this->m_IgnoreList);
m_helper->SetAllowUndefPixels(this->m_allowUndefPixels);
m_helper->SetAllowUnregPixels(this->m_allowUnregPixels);
m_helper->SetErrorValue(this->m_errorValue);
m_helper->SetPaddingValue(this->m_paddingValue);
m_helper->SetInterpolatorType(this->m_InterpolatorType);
m_helper->AddObserver(::map::events::AnyMatchPointEvent(), m_spCommand);
m_helper->AddObserver(::itk::ProgressEvent(), m_spCommand);
// perform registration
m_spMappedImageNode = m_helper->GetRegisteredImage();
// wrap the registration in a data node
if (m_spMappedImageNode.IsNull())
{
emit Error(QString("Error. No registration was determined. No results to store."));
}
else
{
emit ResultIsAvailable(m_spMappedImageNode, this);
}
}
catch (::std::exception &e)
{
emit Error(QString("Error while registering data. Details: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when registering data."));
+ emit Error(QString("Unknown error when registering data."));
}
emit Finished();
};
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h
index 14f486b92f..4ddb75cdd3 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h
@@ -1,100 +1,100 @@
/*============================================================================
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 QmitkFramesRegistrationJob_h
#define QmitkFramesRegistrationJob_h
// QT
#include <QObject>
#include <QRunnable>
// ITK
#include <itkCommand.h>
// MITK
#include <QmitkMappingJob.h>
#include <mitkDataNode.h>
#include <mitkImage.h>
// MatchPoint
#include <mapDeploymentDLLInfo.h>
#include <mapIterativeAlgorithmInterface.h>
#include <mapMultiResRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmBase.h>
#include <mapRegistrationBase.h>
// Map4CTK
#include "mitkUIDHelper.h"
#include <mitkTimeFramesRegistrationHelper.h>
#include <MitkMatchPointRegistrationUIExports.h>
-/** Simple helper job class that could be used to process a frame registration in a paralell thread.
+/** Simple helper job class that could be used to process a frame registration in a parallel thread.
* This is e.g. used be plugins to keep the GUI responsive while doing a frame registration*/
class MITKMATCHPOINTREGISTRATIONUI_EXPORT QmitkFramesRegistrationJob : public QObject,
public QRunnable,
public QmitkMappingJobSettings
{
// this is needed for all Qt objects that should have a Qt meta-object
// (everything that derives from QObject and wants to have signal/slots)
Q_OBJECT
public:
QmitkFramesRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm);
~QmitkFramesRegistrationJob() override;
void run() override;
signals:
void Finished();
void Error(QString err);
void ResultIsAvailable(mitk::Image::Pointer spResult, const QmitkFramesRegistrationJob *pJob);
void AlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration);
void LevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel);
void AlgorithmStatusChanged(QString info);
void AlgorithmInfo(QString info);
void FrameProcessed(double progress);
void FrameRegistered(double progress);
void FrameMapped(double progress);
public:
// Inputs
mitk::BaseData::ConstPointer m_spTargetData;
mitk::Image::ConstPointer m_spTargetMask;
// job settings
mitk::TimeFramesRegistrationHelper::IgnoreListType m_IgnoreList;
mitk::NodeUIDType m_TargetDataUID;
mitk::NodeUIDType m_TargetMaskDataUID;
const map::algorithm::RegistrationAlgorithmBase *GetLoadedAlgorithm() const;
private:
typedef map::algorithm::facet::IterativeAlgorithmInterface IIterativeAlgorithm;
typedef map::algorithm::facet::MultiResRegistrationAlgorithmInterface IMultiResAlgorithm;
mitk::Image::Pointer m_spMappedImageNode;
::itk::MemberCommand<QmitkFramesRegistrationJob>::Pointer m_spCommand;
unsigned long m_ObserverID;
map::algorithm::RegistrationAlgorithmBase::Pointer m_spLoadedAlgorithm;
mitk::TimeFramesRegistrationHelper::Pointer m_helper;
// Helper functions
const mitk::Image *GetTargetDataAsImage() const;
void OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event);
};
#endif
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h
index 216bdba330..e5f81596bf 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h
@@ -1,72 +1,72 @@
/*============================================================================
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 QmitkMAPAlgorithmModel_h
#define QmitkMAPAlgorithmModel_h
#include <QAbstractTableModel>
#include <QStringList>
// MITK
#include "MitkMatchPointRegistrationUIExports.h"
// MatchPoint
#include <mapMetaPropertyAlgorithmInterface.h>
#include <mapRegistrationAlgorithmBase.h>
/*!
\class QmitkMAPAlgorithmModel
Helper class that implements a model to handle the MetaProperty interface of a MatchPoint algorithm
- in contect of the QT view-model-concept. A algorithm can be set as data source for the model.
+ in context of the QT view-model-concept. A algorithm can be set as data source for the model.
The model retrieves all information through the MetaPropertyInterface. Changes in the view will
be propagated by the model into the algorithm.
\remarks The model only keep a simple pointer to the MetaPropertyInterface of the algorithm.
You have to ensure to reset the algorithm if the pointer goes invalid.
\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation.
*/
class MITKMATCHPOINTREGISTRATIONUI_EXPORT QmitkMAPAlgorithmModel : public QAbstractTableModel
{
Q_OBJECT
public:
QmitkMAPAlgorithmModel(QObject *parent = nullptr);
~QmitkMAPAlgorithmModel() override{};
void SetAlgorithm(map::algorithm::RegistrationAlgorithmBase *pAlgorithm);
void SetAlgorithm(map::algorithm::facet::MetaPropertyAlgorithmInterface *pMetaInterface);
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
void UpdateMetaProperties() const;
/** Method uses m_pMetaInterface to retrieve the MetaProperty and unwraps it into an
* suitable QVariant depending on the passed QT role. If the MetaProperty type is not supported, the QVariant is
* invalid.
*/
QVariant GetPropertyValue(const map::algorithm::MetaPropertyInfo *pInfo, int role) const;
template <typename TValueType>
bool CheckCastAndSetProp(const map::algorithm::MetaPropertyInfo *pInfo, const QVariant &value);
bool SetPropertyValue(const map::algorithm::MetaPropertyInfo *pInfo, const QVariant &value);
map::algorithm::facet::MetaPropertyAlgorithmInterface *m_pMetaInterface;
mutable map::algorithm::facet::MetaPropertyAlgorithmInterface::MetaPropertyVectorType m_MetaProperties;
};
#endif
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui
index aa2465cde2..27efbd150a 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui
@@ -1,304 +1,304 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkMapperSettingsWidget</class>
<widget class="QWidget" name="QmitkMapperSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="m_groupAllowUndefPixels">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be defined in the mapped image because they are outside of the field of view of the used input image.&lt;/p&gt;&lt;p&gt;The pixels will be marked with the given padding value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of undefined pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
<string>Allow undefined pixels</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Padding value:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_sbPaddingValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Pixel value that indicates pixels that are outside of the input image</string>
</property>
<property name="minimum">
<number>-5000</number>
</property>
<property name="maximum">
<number>5000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="m_groupAllowUnregPixels">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be registred because they are outside of the field of view of the used registration. The location in the correlated input image pixel(s) are therefore unkown. The pixels will be marked witrh the given error value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of unregistered pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be registered because they are outside of the field of view of the used registration. The location in the correlated input image pixel(s) are therefore unknown. The pixels will be marked with the given error value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of unregistered pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
- <string>Allow unregistred pixels</string>
+ <string>Allow unregistered pixels</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Error value:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_sbErrorValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Value of pixels that cannot be registered because of an unsufficient field of view of the selected registration instance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>-5000</number>
</property>
<property name="maximum">
<number>5000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Interpolator:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_comboInterpolator">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Interpolation function that should be used to map the pixel values from the input image into the result image.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Nearest Neighbor</string>
</property>
</item>
<item>
<property name="text">
<string>Linear</string>
</property>
</item>
<item>
<property name="text">
<string>BSpline (3rd order)</string>
</property>
</item>
<item>
<property name="text">
<string>Windowed Sinc (Hamming)</string>
</property>
</item>
<item>
<property name="text">
<string>Windowed Sinc (Welch)</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QGroupBox" name="m_groupActivateSampling">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Activate super/sub sampling</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>5</number>
</property>
<item>
<widget class="QCheckBox" name="m_cbLinkFactors">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Check to ensure that x, y and z dimension use the same sampling factor.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>linked factors</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>x:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbXFactor">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the sampling factor to change the resolution.&lt;/p&gt;&lt;p&gt;2.0: doubled resolution; e.g. 100 pixels -&amp;gt; 200 pixels and spacing 1 -&amp;gt; spacing 0.5&lt;/p&gt;&lt;p&gt;0.5: half resolution; e.g. 100 pixels -&amp;gt; 50 pixels and spacing 1 -&amp;gt; spacing 2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>y:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbYFactor"/>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>z:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbZFactor"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp
index a79e39b1fc..a5d2c1c523 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp
@@ -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.
============================================================================*/
#include "QmitkMappingJob.h"
// Mitk
#include <mitkImageAccessByItk.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMatchPointPropertyTags.h>
#include <mitkPointSetMappingHelper.h>
#include <mitkProperties.h>
// Qt
#include <QThreadPool>
#include <mapEvents.h>
QmitkMappingJobSettings::QmitkMappingJobSettings()
{
m_doGeometryRefinement = false;
m_MappedName = "";
m_allowUndefPixels = true;
m_paddingValue = 0;
m_allowUnregPixels = true;
m_errorValue = 0;
m_InterpolatorType = mitk::ImageMappingInterpolator::Linear;
};
const mitk::Image *QmitkMappingJob::GetInputDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spInputData.GetPointer());
};
const mitk::PointSet *QmitkMappingJob::GetInputDataAsPointSet() const
{
return dynamic_cast<const mitk::PointSet *>(m_spInputData.GetPointer());
};
const map::core::RegistrationBase *QmitkMappingJob::GetRegistration() const
{
const mitk::MAPRegistrationWrapper *wrapper =
dynamic_cast<const mitk::MAPRegistrationWrapper *>(m_spRegNode->GetData());
return dynamic_cast<const map::core::RegistrationBase *>(wrapper->GetRegistration());
};
void QmitkMappingJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event)
{
const map::events::AnyMatchPointEvent *pMAPEvent = dynamic_cast<const map::events::AnyMatchPointEvent *>(&event);
if (pMAPEvent)
{
emit AlgorithmInfo(QString::fromStdString(pMAPEvent->getComment()));
}
}
QmitkMappingJob::QmitkMappingJob()
{
m_spRefGeometry = nullptr;
m_spCommand = ::itk::MemberCommand<QmitkMappingJob>::New();
m_spCommand->SetCallbackFunction(this, &QmitkMappingJob::OnMapAlgorithmEvent);
};
QmitkMappingJob::~QmitkMappingJob(){};
void QmitkMappingJob::run()
{
const mitk::Image *inputImage = this->GetInputDataAsImage();
const mitk::PointSet *inputSet = this->GetInputDataAsPointSet();
m_spMappedData = nullptr;
if (m_doGeometryRefinement)
{
try
{
mitk::Image::Pointer spResultImage = nullptr;
if (inputImage)
{
spResultImage = mitk::ImageMappingHelper::refineGeometry(inputImage, this->GetRegistration(), true);
}
m_spMappedData = spResultImage;
if (spResultImage.IsNotNull())
{
emit MapResultIsAvailable(spResultImage.GetPointer(), this);
}
else
{
emit Error(QString("Error when when refining image geometry."));
}
}
catch (std::exception &e)
{
emit Error(QString("Error when refining image geometry. Error description: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
emit Error(QString("Unknown error when refining image geometry."));
}
}
else
{
try
{
mitk::BaseData::Pointer spResultData = nullptr;
if (inputImage)
{
spResultData = mitk::ImageMappingHelper::map(this->GetInputDataAsImage(),
this->GetRegistration(),
!(this->m_allowUndefPixels),
this->m_paddingValue,
this->m_spRefGeometry,
!(this->m_allowUnregPixels),
this->m_errorValue,
this->m_InterpolatorType)
.GetPointer();
}
else if (inputSet)
{
mitk::PointSet::PointDataType errorValue;
errorValue.id = -1;
errorValue.pointSpec = mitk::PTUNDEFINED;
errorValue.selected = false;
spResultData = mitk::PointSetMappingHelper::map(inputSet, this->GetRegistration(), -1, false, errorValue);
}
if (spResultData.IsNotNull())
{
emit MapResultIsAvailable(spResultData, this);
}
else
{
emit Error(QString("Error when mapping input data to result."));
}
m_spMappedData = spResultData;
}
catch (std::exception &e)
{
emit Error(QString("Error when mapping data. Error description: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when mapping data."));
+ emit Error(QString("Unknown error when mapping data."));
}
}
};
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp
index 76197de9b9..6327b1912f 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp
@@ -1,200 +1,200 @@
/*============================================================================
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 "QmitkRegistrationJob.h"
// Mitk
#include <mitkMAPAlgorithmHelper.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageMappingHelper.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMaskedAlgorithmHelper.h>
#include <mitkMatchPointPropertyTags.h>
#include <mitkUIDHelper.h>
// Qt
#include <QThreadPool>
// MatchPoint
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
const mitk::Image *QmitkRegistrationJob::GetTargetDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spTargetData.GetPointer());
}
const mitk::Image *QmitkRegistrationJob::GetMovingDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spMovingData.GetPointer());
}
const map::algorithm::RegistrationAlgorithmBase *QmitkRegistrationJob::GetLoadedAlgorithm() const
{
return m_spLoadedAlgorithm;
}
void QmitkRegistrationJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event)
{
const map::events::AlgorithmEvent *pAlgEvent = dynamic_cast<const map::events::AlgorithmEvent *>(&event);
const map::events::AlgorithmIterationEvent *pIterationEvent =
dynamic_cast<const map::events::AlgorithmIterationEvent *>(&event);
const map::events::AlgorithmWrapperEvent *pWrapEvent =
dynamic_cast<const map::events::AlgorithmWrapperEvent *>(&event);
const map::events::AlgorithmResolutionLevelEvent *pLevelEvent =
dynamic_cast<const map::events::AlgorithmResolutionLevelEvent *>(&event);
const map::events::InitializingAlgorithmEvent *pInitEvent =
dynamic_cast<const map::events::InitializingAlgorithmEvent *>(&event);
const map::events::StartingAlgorithmEvent *pStartEvent =
dynamic_cast<const map::events::StartingAlgorithmEvent *>(&event);
const map::events::StoppingAlgorithmEvent *pStoppingEvent =
dynamic_cast<const map::events::StoppingAlgorithmEvent *>(&event);
const map::events::StoppedAlgorithmEvent *pStoppedEvent =
dynamic_cast<const map::events::StoppedAlgorithmEvent *>(&event);
const map::events::FinalizingAlgorithmEvent *pFinalizingEvent =
dynamic_cast<const map::events::FinalizingAlgorithmEvent *>(&event);
const map::events::FinalizedAlgorithmEvent *pFinalizedEvent =
dynamic_cast<const map::events::FinalizedAlgorithmEvent *>(&event);
if (pInitEvent)
{
emit AlgorithmStatusChanged(QString("Initializing algorithm ..."));
}
else if (pStartEvent)
{
emit AlgorithmStatusChanged(QString("Starting algorithm ..."));
}
else if (pStoppingEvent)
{
emit AlgorithmStatusChanged(QString("Stopping algorithm ..."));
}
else if (pStoppedEvent)
{
emit AlgorithmStatusChanged(QString("Stopped algorithm ..."));
if (!pStoppedEvent->getComment().empty())
{
emit AlgorithmInfo(QString("Stopping condition: ") + QString::fromStdString(pStoppedEvent->getComment()));
}
}
else if (pFinalizingEvent)
{
emit AlgorithmStatusChanged(QString("Finalizing algorithm and results ..."));
}
else if (pFinalizedEvent)
{
emit AlgorithmStatusChanged(QString("Finalized algorithm ..."));
}
else if (pIterationEvent)
{
const IIterativeAlgorithm *pIterative =
dynamic_cast<const IIterativeAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::IterativeAlgorithmInterface::IterationCountType count = 0;
bool hasCount = false;
if (pIterative && pIterative->hasIterationCount())
{
hasCount = true;
count = pIterative->getCurrentIteration();
}
emit AlgorithmIterated(QString::fromStdString(pIterationEvent->getComment()), hasCount, count);
}
else if (pLevelEvent)
{
const IMultiResAlgorithm *pResAlg =
dynamic_cast<const IMultiResAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::MultiResRegistrationAlgorithmInterface::ResolutionLevelCountType count = 0;
bool hasCount = false;
QString info = QString::fromStdString(pLevelEvent->getComment());
if (pResAlg && pResAlg->hasLevelCount())
{
count = pResAlg->getCurrentLevel() + 1;
hasCount = true;
info = QString("Level #") + QString::number(pResAlg->getCurrentLevel() + 1) + QString(" ") + info;
}
emit LevelChanged(info, hasCount, count);
}
else if (pAlgEvent && !pWrapEvent)
{
emit AlgorithmInfo(QString::fromStdString(pAlgEvent->getComment()));
}
}
QmitkRegistrationJob::QmitkRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm)
{
m_MapEntity = false;
m_StoreReg = false;
m_ErrorOccured = false;
m_spLoadedAlgorithm = pAlgorithm;
m_JobName = "Unnamed RegJob";
m_MovingDataUID = "Missing moving UID";
m_TargetDataUID = "Missing target UID";
m_spTargetMask = nullptr;
m_spMovingMask = nullptr;
m_spCommand = ::itk::MemberCommand<QmitkRegistrationJob>::New();
m_spCommand->SetCallbackFunction(this, &QmitkRegistrationJob::OnMapAlgorithmEvent);
m_ObserverID = m_spLoadedAlgorithm->AddObserver(::map::events::AlgorithmEvent(), m_spCommand);
}
QmitkRegistrationJob::~QmitkRegistrationJob()
{
m_spLoadedAlgorithm->RemoveObserver(m_ObserverID);
}
void QmitkRegistrationJob::run()
{
try
{
mitk::MAPAlgorithmHelper helper(m_spLoadedAlgorithm);
mitk::MaskedAlgorithmHelper maskedHelper(m_spLoadedAlgorithm);
//*@TODO Data Check and failure handle
helper.SetData(this->m_spMovingData, this->m_spTargetData);
maskedHelper.SetMasks(this->m_spMovingMask, this->m_spTargetMask);
// perform registration
m_spResultRegistration = helper.GetRegistration();
// wrap the registration in a data node
if (m_spResultRegistration.IsNull())
{
emit Error(QString("Error. No registration was determined. No results to store."));
}
else
{
auto spRegWrapper = mitk::MAPRegistrationWrapper::New(m_spResultRegistration);
emit RegResultIsAvailable(spRegWrapper, this);
}
}
catch (::std::exception &e)
{
emit Error(QString("Error while registering data. Details: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when registering data."));
+ emit Error(QString("Unknown error when registering data."));
}
emit Finished();
}
diff --git a/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp b/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp
index 66eafb2386..baf7f10834 100644
--- a/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp
+++ b/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp
@@ -1,99 +1,99 @@
/*============================================================================
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 <usModuleActivator.h>
#include <usModuleContext.h>
#include <mitkCoreServices.h>
#include <mitkIPropertyDescriptions.h>
#include <mitkIPropertyPersistence.h>
#include <mitkModelFitConstants.h>
#include <mitkScalarListLookupTablePropertySerializer.h>
namespace mitk
{
/*
* This is the module activator for the IO aspects of the "ModelFit" module.
*/
class ModelFitIOActivator : public us::ModuleActivator
{
public:
void registerProperty(const std::string& name, const std::string& key, const std::string& description)
{
mitk::CoreServicePointer<mitk::IPropertyDescriptions> propDescService(mitk::CoreServices::GetPropertyDescriptions());
propDescService->AddDescription(name, description);
mitk::PropertyPersistenceInfo::Pointer ppi = mitk::PropertyPersistenceInfo::New();
ppi->SetNameAndKey(name, key);
mitk::CoreServicePointer<mitk::IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
propPersistenceService->AddInfo(ppi, true);
}
void registerProperty(const std::string& name, const std::string& key, const std::string& description, const PropertyPersistenceInfo::DeserializationFunctionType &deFnc, const PropertyPersistenceInfo::SerializationFunctionType &serFnc)
{
mitk::CoreServicePointer<mitk::IPropertyDescriptions> propDescService(mitk::CoreServices::GetPropertyDescriptions());
propDescService->AddDescription(name, description);
mitk::PropertyPersistenceInfo::Pointer ppi = mitk::PropertyPersistenceInfo::New();
ppi->SetNameAndKey(name, key);
ppi->SetDeserializationFunction(deFnc);
ppi->SetSerializationFunction(serFnc);
mitk::CoreServicePointer<mitk::IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
propPersistenceService->AddInfo(ppi, true);
}
void Load(us::ModuleContext* /*context*/) override
{
//register relevant properties
registerProperty(mitk::ModelFitConstants::INPUT_VARIABLES_PROPERTY_NAME(), "modelfit_input_variables", "Array of input variables used in/for a model fit.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML);
registerProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME(), "modelfit_parameter_name", "Name of the parameter, that is represented by the data and how it is used in the function string (modelfit.model.function).");
registerProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME(), "modelfit_parameter_unit", "Unit string of the Parameter. Is only used for display. Default value: \"\" (no unit)");
registerProperty(mitk::ModelFitConstants::PARAMETER_SCALE_PROPERTY_NAME(), "modelfit_parameter_scale", "Scaling of the parameter. Default value: 1.");
registerProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME(), "modelfit_parameter_type", "Type of the parameters. Default value: parameter. Other options: derived, criterion, evaluation.");
- registerProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME(), "modelfit_model_type", "Value specifies the type of model (helpfull for classification; e.g. MR perfusion)");
+ registerProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME(), "modelfit_model_type", "Value specifies the type of model (helpful for classification; e.g. MR perfusion)");
registerProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME(), "modelfit_model_name", "Name of the specific fit. Only used for display.");
registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME(), "modelfit_model_function", "Function string, that specifies the model and will be parsed, to plot the curves based on the parameter. Optional parameter that must not be set.");
registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME(), "modelfit_model_functionClass", "ID of the model class implementation.");
registerProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME(), "modelfit_model_x", "Name of the forumar parameter 'x', that is specified on the x axis. Only needed if model function string is set and formular must be parsed.");
registerProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME(), "modelfit_xaxis_name", "Display name of the x axis.");
registerProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME(), "modelfit_xaxis_unit", "Unit of the x axis.");
registerProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME(), "modelfit_yaxis_name", "Display name of the y axis.");
registerProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME(), "modelfit_yaxis_unit", "Unit of the y axis.");
registerProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME(), "modelfit_fit_uid", "UID of the fit.");
registerProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME(), "modelfit_fit_name", "Human readable name for the fit.");
registerProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME(), "modelfit_fit_type", "Type of the fit (e.g. ROI based or pixel based).");
registerProperty(mitk::ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME(), "modelfit_fit_input_roiUID", "UID of the ROI used for the fit.");
registerProperty(mitk::ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME(), "modelfit_fit_input_data", "Property containing input data directly stored in the fit information.");
registerProperty(mitk::ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME(), "modelfit_fit_staticParameters", "Property containing static parameters used for the fit.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML);
//legacy properties for backwards compatibility
registerProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME(), "data_uid", "UID used to identify data in an MITK session.");
registerProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME(), "modelfit_fit_input_imageUID", "UID of the input image that used to fit the model.");
}
void Unload(us::ModuleContext* ) override
{
}
};
}
US_EXPORT_MODULE_ACTIVATOR(mitk::ModelFitIOActivator)
diff --git a/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp b/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp
index de03cf4055..03fe3bc2d1 100644
--- a/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp
+++ b/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp
@@ -1,167 +1,167 @@
/*============================================================================
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.
============================================================================*/
// std includes
#include <string>
#include <numeric>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkTemporalJoinImagesFilter.h>
mitkCommandLineParser::StringContainerType inFilenames;
std::string outFileName;
std::vector<mitk::Image::Pointer> images;
std::vector<mitk::TimePointType> timebounds;
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Dynamic Data Analysis Tools");
parser.setTitle("Fuse 3D to 4D Image");
parser.setDescription("MiniApp that allows to fuse several 3D images (with same geometry) into a 3D+t (4D) image that can be processed as dynamic data.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
- "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be fused", us::Any(), false, false, false, mitkCommandLineParser::Input);
+ "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Paths to the input images that should be fused", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
"Path to the fused 3D+t image.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
- "time", "t", mitkCommandLineParser::StringList, "Time bounds", "Defines the time geometry of the resulting dynamic image in [ms]. The first number is the start time point of the first time step. All other numbers are the max bound of a time step. So the structure is [minBound0 maxBound1 [maxBound2 [... maxBoundN]]]; e.g. \"2 3.5 10\" encodes a time geometry with two time steps and that starts at 2 ms and the second time step starts at 3.5 ms and ends at 10 ms. If not set e propertional time geometry with 1 ms duration will be generated!", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "time", "t", mitkCommandLineParser::StringList, "Time bounds", "Defines the time geometry of the resulting dynamic image in [ms]. The first number is the start time point of the first time step. All other numbers are the max bound of a time step. So the structure is [minBound0 maxBound1 [maxBound2 [... maxBoundN]]]; e.g. \"2 3.5 10\" encodes a time geometry with two time steps and that starts at 2 ms and the second time step starts at 3.5 ms and ends at 10 ms. If not set e proportional time geometry with 1 ms duration will be generated!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
inFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["inputs"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("time"))
{
auto timeBoundsStr = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["time"]);
for (const auto& timeBoundStr : timeBoundsStr)
{
std::istringstream stream;
stream.imbue(std::locale("C"));
stream.str(timeBoundStr);
mitk::TimePointType time = 0 ;
stream >> time;
timebounds.emplace_back(time);
}
}
return true;
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
if (timebounds.empty())
{
timebounds.resize(inFilenames.size()+1);
std::iota(timebounds.begin(), timebounds.end(), 0.0);
}
else if (inFilenames.size() + 1 != timebounds.size())
{
std::cerr << "Cannot fuse images. Explicitly specified time bounds do not match. Use --help for more information on how to specify time bounds correctly.";
return EXIT_FAILURE;
};
//! [do processing]
try
{
std::cout << "Load images:" << std::endl;
auto filter = mitk::TemporalJoinImagesFilter::New();
unsigned int step = 0;
for (auto path : inFilenames)
{
std::cout << "Time step #"<<step<<" @ "<<timebounds[step]<< " ms: " << path << std::endl;
auto image = mitk::IOUtil::Load<mitk::Image>(path, &readerFilterFunctor);
images.push_back(image);
filter->SetInput(step, image);
++step;
}
filter->SetFirstMinTimeBound(timebounds[0]);
filter->SetMaxTimeBounds({ timebounds.begin() + 1, timebounds.end() });
std::cout << "Fuse the images ..." << std::endl;
filter->Update();
auto output = filter->GetOutput();
std::cout << "Save output image: " << outFileName << std::endl;
mitk::IOUtil::Save(output, outFileName);
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp b/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp
index 0e45f362ed..49709c4a9a 100644
--- a/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp
+++ b/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp
@@ -1,360 +1,360 @@
/*============================================================================
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.
============================================================================*/
// std includes
#include <string>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPixelBasedParameterFitImageGenerator.h>
#include <mitkROIBasedParameterFitImageGenerator.h>
#include <mitkLinearModelParameterizer.h>
#include <mitkGenericParamModelParameterizer.h>
#include <mitkModelFitInfo.h>
#include <mitkMaskedDynamicImageStatisticsGenerator.h>
#include <mitkLevenbergMarquardtModelFitFunctor.h>
#include <mitkNormalizedSumOfSquaredDifferencesFitCostFunction.h>
#include <mitkExtractTimeGrid.h>
#include <mitkModelFitCmdAppsHelper.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
std::string inFilename;
std::string outFileName;
std::string maskFileName;
bool verbose(false);
bool roibased(false);
std::string functionName;
std::string formular;
mitk::Image::Pointer image;
mitk::Image::Pointer mask;
void onFitEvent(::itk::Object* caller, const itk::EventObject & event, void* /*data*/)
{
itk::ProgressEvent progressEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast<mitk::ParameterFitImageGeneratorBase*>(caller);
std::cout <<castedReporter->GetProgress()*100 << "% ";
}
}
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Dynamic Data Analysis Tools");
parser.setTitle("Generic Fitting");
parser.setDescription("MiniApp that allows to make a pixel based fitting on the intensity signal over time for a given model function.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Model parameters");
parser.addArgument(
"function", "f", mitkCommandLineParser::String, "Model function", "Function that should be used to fit the intensity signals. Options are: \"Linear\" or \"<Parameter Number>\" (for generic formulas).", us::Any(std::string("Linear")));
parser.addArgument(
"formular", "y", mitkCommandLineParser::String, "Generic model function formular", "Formular of a generic model (if selected) that will be parsed and fitted.", us::Any());
parser.endGroup();
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file template",
"where to save the output parameter images. The specified path will be used as template to determine the format (via extension) and the name \"root\". For each parameter a suffix will be added to the name.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be fitted. Must have the same geometry as the input image!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output");
parser.addArgument(
- "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intesity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
+ "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intensity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
// parse, cast and set required arguments
functionName = "Linear";
if (parsedArgs.count("function"))
{
functionName = us::any_cast<std::string>(parsedArgs["function"]);
}
if (parsedArgs.count("formular"))
{
formular = us::any_cast<std::string>(parsedArgs["formular"]);
}
inFilename = us::any_cast<std::string>(parsedArgs["input"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
verbose = false;
if (parsedArgs.count("verbose"))
{
verbose = us::any_cast<bool>(parsedArgs["verbose"]);
}
roibased = false;
if (parsedArgs.count("roibased"))
{
roibased = us::any_cast<bool>(parsedArgs["roibased"]);
}
if (parsedArgs.count("mask"))
{
maskFileName = us::any_cast<std::string>(parsedArgs["mask"]);
}
return true;
}
void configureInitialParametersOfParameterizer(mitk::ModelParameterizerBase*
parameterizer)
{
mitk::GenericParamModelParameterizer* genericParameterizer =
dynamic_cast<mitk::GenericParamModelParameterizer*>(parameterizer);
if (genericParameterizer)
{
genericParameterizer->SetFunctionString(formular);
}
}
mitk::ModelFitFunctorBase::Pointer createDefaultFitFunctor(
const mitk::ModelParameterizerBase* parameterizer)
{
mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor =
mitk::LevenbergMarquardtModelFitFunctor::New();
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 =
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New();
fitFunctor->RegisterEvaluationParameter("Chi^2", chi2);
mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel();
::itk::LevenbergMarquardtOptimizer::ScalesType scales;
scales.SetSize(refModel->GetNumberOfParameters());
scales.Fill(1.0);
fitFunctor->SetScales(scales);
fitFunctor->SetDebugParameterMaps(true);
return fitFunctor.GetPointer();
}
template <typename TParameterizer>
void generateModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
/*modelFitInfo*/, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
configureInitialParametersOfParameterizer(modelParameterizer);
//Specify fitting strategy and criterion parameters
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask);
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
}
template <typename TParameterizer>
void generateModelFit_ROIBased(
mitk::modelFit::ModelFitInfo::Pointer& /*modelFitInfo*/,
mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
configureInitialParametersOfParameterizer(modelParameterizer);
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Specify fitting strategy and criterion parameters
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
}
void doFitting()
{
mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;
::itk::CStyleCommand::Pointer command = ::itk::CStyleCommand::New();
command->SetCallback(onFitEvent);
bool isLinearFactory = functionName == "Linear";
if (isLinearFactory)
{
std::cout << "Model: linear" << std::endl;
if (!roibased)
{
generateModelFit_PixelBased<mitk::LinearModelParameterizer>(fitSession, generator);
}
else
{
generateModelFit_ROIBased<mitk::LinearModelParameterizer>(fitSession, generator);
}
}
else
{
std::cout << "Model: generic (2 parameter)" << std::endl;
if (!roibased)
{
generateModelFit_PixelBased<mitk::GenericParamModelParameterizer>(fitSession, generator);
}
else
{
generateModelFit_ROIBased<mitk::GenericParamModelParameterizer>(fitSession, generator);
}
}
if (generator.IsNotNull() )
{
std::cout << "Started fitting process..." << std::endl;
generator->AddObserver(::itk::AnyEvent(), command);
generator->Generate();
std::cout << std::endl << "Finished fitting process" << std::endl;
mitk::storeModelFitGeneratorResults(outFileName, generator, fitSession);
}
else
{
- mitkThrow() << "Fitting error! Could not initalize fitting job.";
+ mitkThrow() << "Fitting error! Could not initialize fitting job.";
}
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
//! [do processing]
try
{
image = mitk::IOUtil::Load<mitk::Image>(inFilename, &readerFilterFunctor);
std::cout << "Input: " << inFilename << std::endl;
if (!maskFileName.empty())
{
mask = mitk::IOUtil::Load<mitk::Image>(maskFileName, &readerFilterFunctor);
std::cout << "Mask: " << maskFileName << std::endl;
}
else
{
std::cout << "Mask: none" << std::endl;
}
if (roibased && mask.IsNull())
{
mitkThrow() << "Error. Cannot fit. Please specify mask if you select roi based fitting.";
}
std::cout << "Style: ";
if (roibased)
{
std::cout << "ROI based";
}
else
{
std::cout << "pixel based";
}
std::cout << std::endl;
doFitting();
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const itk::ExceptionObject& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp b/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp
index d3323ed319..4535644ecc 100644
--- a/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp
+++ b/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp
@@ -1,447 +1,447 @@
/*============================================================================
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.
============================================================================*/
// std includes
#include <string>
// itk includes
#include "itksys/SystemTools.hxx"
#include "itkImageRegionConstIteratorWithIndex.h"
#include "itkCastImageFilter.h"
#include "itkExtractImageFilter.h"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkImageTimeSelector.h>
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
mitkCommandLineParser::StringContainerType inFilenames;
std::string outFileName;
std::string maskFileName;
mitkCommandLineParser::StringContainerType captions;
using ImageVectorType = std::vector<mitk::Image::Pointer>;
ImageVectorType images;
mitk::Image::Pointer mask;
bool verbose(false);
typedef itk::Image<mitk::ScalarType, 3> InternalImageType;
typedef std::map<std::string, InternalImageType::Pointer> InternalImageMapType;
InternalImageMapType internalImages;
itk::ImageRegion<3> relevantRegion;
InternalImageType::PointType relevantOrigin;
InternalImageType::SpacingType relevantSpacing;
InternalImageType::DirectionType relevantDirection;
typedef itk::Index<3> DumpIndexType;
typedef std::vector<mitk::ScalarType> DumpedValuesType;
struct DumpIndexCompare
{
bool operator() (const DumpIndexType& lhs, const DumpIndexType& rhs) const
{
if (lhs[0] < rhs[0])
{
return true;
}
else if (lhs[0] > rhs[0])
{
return false;
}
if (lhs[1] < rhs[1])
{
return true;
}
else if (lhs[1] > rhs[1])
{
return false;
}
return lhs[2] < rhs[2];
}
};
typedef std::map<DumpIndexType, DumpedValuesType, DumpIndexCompare> DumpPixelMapType;
DumpPixelMapType dumpedPixels;
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Generic Analysis Tools");
parser.setTitle("Pixel Dumper");
parser.setDescription("MiniApp that allows to dump the pixel values of all passed files into a csv. The region of dumping can defined by a mask. All images (and mask) must have the same geometrie.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"inputs", "i", mitkCommandLineParser::StringList, "Input files", "list of the images that should be dumped.", us::Any(), false);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file",
"where to save the csv.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be dumped. Must have the same geometry as the input images!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "captions", "c", mitkCommandLineParser::StringList, "Captions of image columns", "If provided the pixel columns of the csv will be named according to the passed values instead of using the image pathes. Number of images and names must be equal.", us::Any(), false);
+ "captions", "c", mitkCommandLineParser::StringList, "Captions of image columns", "If provided the pixel columns of the csv will be named according to the passed values instead of using the image paths. Number of images and names must be equal.", us::Any(), false);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
// parse, cast and set required arguments
inFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["inputs"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("mask"))
{
maskFileName = us::any_cast<std::string>(parsedArgs["mask"]);
}
captions = inFilenames;
if (parsedArgs.count("captions"))
{
captions = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["captions"]);
}
return true;
}
template < typename TPixel, unsigned int VImageDimension >
void ExtractRelevantInformation(
const itk::Image< TPixel, VImageDimension > *image)
{
relevantRegion = image->GetLargestPossibleRegion();
relevantOrigin = image->GetOrigin();
relevantSpacing = image->GetSpacing();
relevantDirection = image->GetDirection();
}
template < typename TPixel, unsigned int VImageDimension >
void DoInternalImageConversion(
const itk::Image< TPixel, VImageDimension > *image,
InternalImageType::Pointer& internalImage)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
//check if image fit to geometry
// Make sure that spacing are the same
typename ImageType::SpacingType imageSpacing = image->GetSpacing();
typename ImageType::PointType zeroPoint; zeroPoint.Fill(0.0);
if ((zeroPoint + imageSpacing).SquaredEuclideanDistanceTo((zeroPoint + relevantSpacing)) >
1e-6) // for the dumper we are not as strict as mitk normally would be (mitk::eps)
{
mitkThrow() << "Images need to have same spacing! (Image spacing: " << imageSpacing
<< "; relevant spacing: " << relevantSpacing << ")";
}
// Make sure that orientation of mask and image are the same
typename ImageType::DirectionType imageDirection = image->GetDirection();
for (unsigned int i = 0; i < imageDirection.RowDimensions; ++i)
{
for (unsigned int j = 0; j < imageDirection.ColumnDimensions; ++j)
{
double differenceDirection = imageDirection[i][j] - relevantDirection[i][j];
if (fabs(differenceDirection) > 1e-6) // SD: 1e6 wird hier zum zweiten mal als Magic Number benutzt -> Konstante
{
// for the dumper we are not as strict as mitk normally would be (mitk::eps)
mitkThrow() << "Images need to have same direction! (Image direction: "
<< imageDirection << "; relevant direction: " << relevantDirection << ")";
}
}
}
// Make sure that origin of mask and image are the same
typename ImageType::PointType imageOrigin = image->GetOrigin();
if (imageOrigin.SquaredEuclideanDistanceTo(relevantOrigin) > 1e-6)
{
// for the dumper we are not as strict as mitk normally would be (mitk::eps)
mitkThrow() << "Image need to have same spacing! (Image spacing: "
<< imageSpacing << "; relevant spacing: " << relevantOrigin << ")";
}
typename ImageType::RegionType imageRegion = image->GetLargestPossibleRegion();
if (!imageRegion.IsInside(relevantRegion) && imageRegion != relevantRegion)
{
mitkThrow() << "Images need to have same region! (Image region: "
<< imageRegion << "; relevant region: " << relevantRegion << ")";
}
//convert to internal image
typedef itk::ExtractImageFilter<ImageType, ImageType> ExtractFilterType;
typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
typedef itk::CastImageFilter<ImageType, InternalImageType> CastFilterType;
typename CastFilterType::Pointer castFilter = CastFilterType::New();
extractFilter->SetInput(image);
extractFilter->SetExtractionRegion(relevantRegion);
castFilter->SetInput(extractFilter->GetOutput());
castFilter->Update();
internalImage = castFilter->GetOutput();
}
template < typename TPixel, unsigned int VImageDimension >
void DoMaskedCollecting(
const itk::Image< TPixel, VImageDimension > *image)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
itk::ImageRegionConstIteratorWithIndex<ImageType> it(image, relevantRegion);
it.GoToBegin();
while (!it.IsAtEnd())
{
if (mask.IsNull() || it.Get() > 0)
{
DumpedValuesType values;
const auto index = it.GetIndex();
for (auto& imagePos : internalImages)
{
double value = imagePos.second->GetPixel(index);
values.push_back(value);
}
dumpedPixels.insert(std::make_pair(index, values));
}
++it;
}
}
InternalImageMapType ConvertImageTimeSteps(mitk::Image* image)
{
InternalImageMapType map;
InternalImageType::Pointer internalImage;
for (unsigned int i = 0; i < image->GetTimeSteps(); ++i)
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(image);
imageTimeSelector->SetTimeNr(i);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::Image::Pointer imageTimePoint = imageTimeSelector->GetOutput();
AccessFixedDimensionByItk_1(imageTimePoint,
DoInternalImageConversion,
3,
internalImage);
std::stringstream stream;
stream << "[" << i << "]";
map.insert(std::make_pair(stream.str(), internalImage));
}
return map;
}
void doDumping()
{
if (mask.IsNotNull() && mask->GetTimeSteps() > 1)
{
std::cout <<
"Pixel Dumper: Selected mask has multiple timesteps. Only use first timestep to mask the pixel dumping." << std::endl;
mitk::ImageTimeSelector::Pointer maskTimeSelector = mitk::ImageTimeSelector::New();
maskTimeSelector->SetInput(mask);
maskTimeSelector->SetTimeNr(0);
maskTimeSelector->UpdateLargestPossibleRegion();
mask = maskTimeSelector->GetOutput();
}
try
{
if (mask.IsNotNull())
{ // if mask exist, we use the mask because it could be a sub region.
AccessFixedDimensionByItk(mask, ExtractRelevantInformation, 3);
}
else
{
AccessFixedDimensionByItk(images.front(), ExtractRelevantInformation, 3);
}
}
catch (const std::exception& e)
{
std::cerr << "Error extracting image geometry. Error text: " << e.what();
throw;
}
for (unsigned int index = 0; index < images.size(); ++index)
{
try
{
InternalImageMapType conversionMap = ConvertImageTimeSteps(images[index]);
if (conversionMap.size() == 1)
{
internalImages.insert(std::make_pair(captions[index], conversionMap.begin()->second));
}
else if (conversionMap.size() > 1)
{
for (auto& pos : conversionMap)
{
std::stringstream namestream;
namestream << captions[index] << " " << pos.first;
internalImages.insert(std::make_pair(namestream.str(), pos.second));
}
}
}
catch (const std::exception& e)
{
std::stringstream errorStr;
errorStr << "Inconsistent image \"" << captions[index] << "\" will be excluded from the collection. Error: " << e.what();
std::cerr << errorStr.str() << std::endl;
}
}
if (mask.IsNotNull())
{ // if mask exist, we use the mask because it could be a sub region.
AccessFixedDimensionByItk(mask, DoMaskedCollecting, 3);
}
else
{
AccessFixedDimensionByItk(images.front(), DoMaskedCollecting, 3);
}
}
void storeCSV()
{
std::ofstream resultfile;
resultfile.open(outFileName.c_str());
resultfile << "x,y,z";
for (auto aImage : internalImages)
{
resultfile << "," << aImage.first;
}
resultfile << std::endl;
for (auto dumpPos : dumpedPixels)
{
resultfile << dumpPos.first[0] << "," << dumpPos.first[1] << "," << dumpPos.first[2];
for(auto d : dumpPos.second)
{
resultfile << "," << d;
}
resultfile << std::endl;
}
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
if (!captions.empty() && inFilenames.size() != captions.size())
{
std::cerr << "Cannot dump images. Number of given captions does not equal number of given images.";
return EXIT_FAILURE;
};
//! [do processing]
try
{
std::cout << "Load images:" << std::endl;
for (auto path : inFilenames)
{
std::cout << "Input: " << path << std::endl;
auto image = mitk::IOUtil::Load<mitk::Image>(path, &readerFilterFunctor);
images.push_back(image);
}
if (!maskFileName.empty())
{
mask = mitk::IOUtil::Load<mitk::Image>(maskFileName, &readerFilterFunctor);
std::cout << "Mask: " << maskFileName << std::endl;
}
else
{
std::cout << "Mask: none" << std::endl;
}
doDumping();
storeCSV();
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const itk::ExceptionObject& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
index 6f2f9adb75..9177da0232 100644
--- a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
+++ b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
@@ -1,233 +1,233 @@
/*============================================================================
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 __itkMultiOutputNaryFunctorImageFilter_hxx
#define __itkMultiOutputNaryFunctorImageFilter_hxx
#include "itkMultiOutputNaryFunctorImageFilter.h"
#include "itkImageRegionIterator.h"
#include "itkProgressReporter.h"
namespace itk
{
/**
* Constructor
*/
template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage >
MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage >
::MultiOutputNaryFunctorImageFilter()
{
this->DynamicMultiThreadingOff();
// This number will be incremented each time an image
// is added over the two minimum required
this->SetNumberOfRequiredInputs(1);
this->ActualizeOutputs();
}
template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage >
void
MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage >
::ActualizeOutputs()
{
this->SetNumberOfRequiredOutputs(m_Functor.GetNumberOfOutputs());
for (typename Superclass::DataObjectPointerArraySizeType i = this->GetNumberOfIndexedOutputs(); i< m_Functor.GetNumberOfOutputs(); ++i)
{
this->SetNthOutput( i, this->MakeOutput(i) );
}
while(this->GetNumberOfIndexedOutputs() > m_Functor.GetNumberOfOutputs())
{
this->RemoveOutput(this->GetNumberOfIndexedOutputs()-1);
}
};
/**
* ThreadedGenerateData Performs the pixel-wise addition
*/
template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage >
void
MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage >
::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
ThreadIdType threadId)
{
ProgressReporter progress( this, threadId,
outputRegionForThread.GetNumberOfPixels() );
const unsigned int numberOfInputImages =
static_cast< unsigned int >( this->GetNumberOfIndexedInputs() );
const unsigned int numberOfOutputImages =
static_cast< unsigned int >( this->GetNumberOfIndexedOutputs() );
typedef ImageRegionConstIterator< TInputImage > ImageRegionConstIteratorType;
std::vector< ImageRegionConstIteratorType * > inputItrVector;
inputItrVector.reserve(numberOfInputImages);
typedef ImageRegionIterator< TOutputImage > OutputImageRegionIteratorType;
std::vector< OutputImageRegionIteratorType * > outputItrVector;
outputItrVector.reserve(numberOfOutputImages);
//check if mask image is set and generate iterator if mask is valid
typedef ImageRegionConstIterator< TMaskImage > MaskImageRegionIteratorType;
MaskImageRegionIteratorType* pMaskIterator = nullptr;
if (m_Mask.IsNotNull())
{
if (!m_Mask->GetLargestPossibleRegion().IsInside(outputRegionForThread))
{
itkExceptionMacro("Mask of filter is set but does not cover region of thread. Mask region: "<< m_Mask->GetLargestPossibleRegion() <<"Thread region: "<<outputRegionForThread)
}
pMaskIterator = new MaskImageRegionIteratorType(m_Mask,outputRegionForThread);
}
// go through the inputs and add iterators for non-null inputs
for ( unsigned int i = 0; i < numberOfInputImages; ++i )
{
InputImagePointer inputPtr =
dynamic_cast< TInputImage * >( ProcessObject::GetInput(i) );
if ( inputPtr )
{
inputItrVector.push_back( new ImageRegionConstIteratorType(inputPtr, outputRegionForThread) );
}
}
// go through the outputs and add iterators for non-null outputs
for ( unsigned int i = 0; i < numberOfOutputImages; ++i )
{
OutputImagePointer outputPtr =
dynamic_cast< TOutputImage * >( ProcessObject::GetOutput(i) );
if ( outputPtr )
{
outputItrVector.push_back( new OutputImageRegionIteratorType(outputPtr, outputRegionForThread) );
}
}
typename std::vector< ImageRegionConstIteratorType * >::iterator regionInputIterators;
const typename std::vector< ImageRegionConstIteratorType * >::const_iterator regionInputItEnd =
inputItrVector.end();
typename std::vector< OutputImageRegionIteratorType * >::iterator regionOutputIterators;
const typename std::vector< OutputImageRegionIteratorType * >::const_iterator regionOutputItEnd =
outputItrVector.end();
const unsigned int numberOfValidInputImages = inputItrVector.size();
const unsigned int numberOfValidOutputImages = outputItrVector.size();
if ( (numberOfValidInputImages != 0) && ( numberOfValidOutputImages != 0))
{
try
{
while ( !(outputItrVector.front()->IsAtEnd()) )
{
typename NaryInputArrayType::iterator arrayInIt;
typename NaryOutputArrayType::iterator arrayOutIt;
NaryInputArrayType naryInputArray(numberOfValidInputImages);
NaryOutputArrayType naryOutputArray(numberOfValidOutputImages);
bool isValid = true;
if (pMaskIterator)
{
isValid = pMaskIterator->Get() > 0;
++(*pMaskIterator);
}
arrayInIt = naryInputArray.begin();
regionInputIterators = inputItrVector.begin();
typename ImageRegionConstIteratorType::IndexType currentIndex;
if(regionInputIterators != regionInputItEnd)
{
currentIndex = ( *regionInputIterators )->GetIndex();
}
while ( regionInputIterators != regionInputItEnd )
{
*arrayInIt++ = ( *regionInputIterators )->Get();
++( *( *regionInputIterators ) );
++regionInputIterators;
}
if (isValid)
{
naryOutputArray = m_Functor(naryInputArray, currentIndex);
if (numberOfValidOutputImages != naryOutputArray.size())
{
itkExceptionMacro("Error. Number of valid output images do not equal number of outputs required by functor. Number of valid outputs: "<< numberOfValidOutputImages << "; needed output number:" << this->m_Functor.GetNumberOfOutputs());
}
}
else
{
for (typename NaryOutputArrayType::iterator pos = naryOutputArray.begin(); pos!= naryOutputArray.end(); ++pos)
{
*pos = 0.0;
}
}
arrayOutIt = naryOutputArray.begin();
regionOutputIterators = outputItrVector.begin();
while ( regionOutputIterators != regionOutputItEnd )
{
( *regionOutputIterators )->Set(*arrayOutIt++);
++( *( *regionOutputIterators ) );
++regionOutputIterators;
}
progress.CompletedPixel();
}
}
catch(...)
{
// Free memory in case of exceptions
regionInputIterators = inputItrVector.begin();
while ( regionInputIterators != regionInputItEnd )
{
delete ( *regionInputIterators++ );
}
regionOutputIterators = outputItrVector.begin();
while ( regionOutputIterators != regionOutputItEnd )
{
delete ( *regionOutputIterators++ );
}
delete pMaskIterator;
throw;
}
}
- // Free memory regulary
+ // Free memory regularly
regionInputIterators = inputItrVector.begin();
while ( regionInputIterators != regionInputItEnd )
{
delete ( *regionInputIterators++ );
}
regionOutputIterators = outputItrVector.begin();
while ( regionOutputIterators != regionOutputItEnd )
{
delete ( *regionOutputIterators++ );
}
delete pMaskIterator;
}
} // end namespace itk
#endif
diff --git a/Modules/ModelFit/include/mitkExponentialSaturationModel.h b/Modules/ModelFit/include/mitkExponentialSaturationModel.h
index daba982745..76cb11ecec 100644
--- a/Modules/ModelFit/include/mitkExponentialSaturationModel.h
+++ b/Modules/ModelFit/include/mitkExponentialSaturationModel.h
@@ -1,132 +1,132 @@
/*============================================================================
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 __MITK_EXPONENTIAL_SATURATION_MODEL_H_
#define __MITK_EXPONENTIAL_SATURATION_MODEL_H_
#include "mitkModelBase.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** @class ExponentialSaturationModel
- * @brief This genric model has the form: if x<onset: y(x) = baseline , else: y(x) = baseline + (y_final-baseline) * (1 - exp(-rate*(x-onset)))
+ * @brief This generic model has the form: if x<onset: y(x) = baseline , else: y(x) = baseline + (y_final-baseline) * (1 - exp(-rate*(x-onset)))
*/
class MITKMODELFIT_EXPORT ExponentialSaturationModel : public mitk::ModelBase
{
public:
typedef ExponentialSaturationModel Self;
typedef mitk::ModelBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef Superclass::ParameterNameType ParameterNameType;
typedef Superclass::ParametersSizeType ParametersSizeType;
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(ExponentialSaturationModel, ModelBase);
static const std::string NAME_PARAMETER_BAT;
static const std::string NAME_PARAMETER_y_bl;
static const std::string NAME_PARAMETER_y_fin;
static const std::string NAME_PARAMETER_k;
static const unsigned int NUMBER_OF_PARAMETERS;
static const std::string UNIT_PARAMETER_BAT;
static const std::string UNIT_PARAMETER_y_bl;
static const std::string UNIT_PARAMETER_y_fin;
static const std::string UNIT_PARAMETER_k;
static const unsigned int POSITION_PARAMETER_BAT;
static const unsigned int POSITION_PARAMETER_y_bl;
static const unsigned int POSITION_PARAMETER_y_fin;
static const unsigned int POSITION_PARAMETER_k;
static const unsigned int NUMBER_OF_STATIC_PARAMETERS;
static const std::string MODEL_DISPLAY_NAME;
static const std::string MODEL_TYPE;
static const std::string FUNCTION_STRING;
static const std::string X_NAME;
static const std::string X_AXIS_NAME;
static const std::string X_AXIS_UNIT;
static const std::string Y_AXIS_NAME;
static const std::string Y_AXIS_UNIT;
ParameterNamesType GetParameterNames() const override;
ParametersSizeType GetNumberOfParameters() const override;
ParamterUnitMapType GetParameterUnits() const override;
ParameterNamesType GetStaticParameterNames() const override;
ParametersSizeType GetNumberOfStaticParameters() const override;
std::string GetModelDisplayName() const override;
std::string GetModelType() const override;
FunctionStringType GetFunctionString() const override;
std::string GetXName() const override;
std::string GetXAxisName() const override;
std::string GetXAxisUnit() const override;
std::string GetYAxisName() const override;
std::string GetYAxisUnit() const override;
protected:
ExponentialSaturationModel() {};
~ExponentialSaturationModel() override {};
/**
* Actual implementation of the clone method. This method should be reimplemeted
* in subclasses to clone the extra required parameters.
*/
itk::LightObject::Pointer InternalClone() const override;
ModelResultType ComputeModelfunction(const ParametersType& parameters) const override;
void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) override;
StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const override;
private:
//No copy constructor allowed
ExponentialSaturationModel(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h b/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h
index 632d7529de..00a393e7ea 100644
--- a/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h
+++ b/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h
@@ -1,98 +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 mitkGaussianNoiseFunctor_h
#define mitkGaussianNoiseFunctor_h
#include <iostream>
#include <cstdlib>
namespace mitk
{
template <class TInputPixel, class TOutputPixel>
class GaussianNoiseFunctor
{
public:
GaussianNoiseFunctor(): m_Mu(0), m_Sigma(0) {};
~GaussianNoiseFunctor() {};
void SetMean(double mu)
{
this->m_Mu = mu;
}
void SetSigma(double sigma)
{
this->m_Sigma = sigma;
}
bool operator!=(const GaussianNoiseFunctor& other) const
{
return !(*this == other);
}
bool operator==(const GaussianNoiseFunctor& other) const
{
return (this->m_Mu == other.m_Mu) && (this->m_Sigma == other.m_Sigma) ;
}
inline TOutputPixel operator()(const TInputPixel& value) const
{
double n = noise(this->m_Mu, this->m_Sigma);
TOutputPixel result = value + n;
return result;
}
private:
double m_Mu, m_Sigma;
/** @todo #2 Better function?
* This function is meant to generate a random number from a normal distribution with the passed mean and standard deviation.
- * I found this code online, under c++11 there is supposed to be a std - libary for that: std::normal_distribution
+ * I found this code online, under c++11 there is supposed to be a std - library for that: std::normal_distribution
**/
inline double noise(double mu, double sigma) const
{
double u1, u2, W, mult;
static double x1, x2;
static int i = 0;
if (i == 1)
{
i = !i;
return (mu + sigma * (double)x2);
}
do
{
u1 = -1 + (static_cast <double>(rand()) / static_cast <double>(RAND_MAX)) * 2;
u2 = -1 + (static_cast <double>(rand()) / static_cast <double>(RAND_MAX)) * 2;
W = u1 * u1 + u2 * u2;
}
while (W >= 1 || W == 0);
mult = sqrt((-2 * log(W)) / W);
x1 = u1 * mult;
x2 = u2 * mult;
i = !i;
return (mu + sigma * (double)x1);
}
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkGenericParamModel.h b/Modules/ModelFit/include/mitkGenericParamModel.h
index 6803f8801b..140449727a 100644
--- a/Modules/ModelFit/include/mitkGenericParamModel.h
+++ b/Modules/ModelFit/include/mitkGenericParamModel.h
@@ -1,108 +1,108 @@
/*============================================================================
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 mitkGenericParamModel_h
#define mitkGenericParamModel_h
#include "mitkModelBase.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** Model that can parse a user specified function string and uses it as model function
that is represented by the model instance.
The parser used to interpret the string can handle simple mathematical formulas (e.g. "3.5 + a * x * sin(x) - 1 / 2").
The parser is able to recognize:
- sums, differences, products and divisions (a + b, 4 - 3, 2 * x, 9 / 3)
- algebraic signs ( +5, -5)
- exponentiation ( 2 ^ 4 )
- parentheses (3 * (4 + 2))
- following unary functions: abs, exp, sin, cos, tan, sind (sine in degrees), cosd (cosine in degrees), tand (tangent in degrees)
- variables (x, a, b, ... j)
Remark: The variable "x" is reserved. It is the signal position / timepoint.
Remark: The current version supports up to 10 model parameter.
Don't use it for a model parameter that should be deduced by fitting (these are a..j).*/
class MITKMODELFIT_EXPORT GenericParamModel : public mitk::ModelBase
{
public:
typedef GenericParamModel Self;
typedef mitk::ModelBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef Superclass::ParameterNameType ParameterNameType;
typedef Superclass::ParametersSizeType ParametersSizeType;
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(GenericParamModel, ModelBase);
static const std::string NAME_STATIC_PARAMETER_number;
std::string GetModelDisplayName() const override;
std::string GetModelType() const override;
FunctionStringType GetFunctionString() const override;
itkSetStringMacro(FunctionString);
- /**@pre The Number of paremeters must be between 1 and 10.*/
+ /**@pre The Number of parameters must be between 1 and 10.*/
itkSetClampMacro(NumberOfParameters, ParametersSizeType, 1, 10);
std::string GetXName() const override;
ParameterNamesType GetParameterNames() const override;
ParametersSizeType GetNumberOfParameters() const override;
ParameterNamesType GetStaticParameterNames() const override;
ParametersSizeType GetNumberOfStaticParameters() const override;
protected:
GenericParamModel();
~GenericParamModel() override {};
/**
* Actual implementation of the clone method. This method should be reimplemeted
* in subclasses to clone the extra required parameters.
*/
itk::LightObject::Pointer InternalClone() const override;
ModelResultType ComputeModelfunction(const ParametersType& parameters) const override;
void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) override;
StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const override;
private:
/**Function string that should be parsed when computing the model function.*/
FunctionStringType m_FunctionString;
/**Number of parameters the model should offer / the function string contains.*/
ParametersSizeType m_NumberOfParameters;
//No copy constructor allowed
GenericParamModel(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h b/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h
index 6808374681..f27af4fb9f 100644
--- a/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h
+++ b/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h
@@ -1,79 +1,79 @@
/*============================================================================
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 mitkGenericParamModelParameterizer_h
#define mitkGenericParamModelParameterizer_h
#include "mitkConcreteModelParameterizerBase.h"
#include "mitkGenericParamModel.h"
namespace mitk
{
/** Parameterizer for the GenricParamModel.
*/
class MITKMODELFIT_EXPORT GenericParamModelParameterizer : public ConcreteModelParameterizerBase
<GenericParamModel>
{
public:
typedef GenericParamModelParameterizer Self;
typedef ConcreteModelParameterizerBase<GenericParamModel> Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(GenericParamModelParameterizer, ConcreteModelParameterizerBase);
itkFactorylessNewMacro(Self);
typedef typename Superclass::ModelBaseType ModelBaseType;
typedef typename Superclass::ModelBasePointer ModelBasePointer;
typedef typename Superclass::ModelType ModelType;
typedef typename Superclass::ModelPointer ModelPointer;
typedef typename Superclass::StaticParameterValueType StaticParameterValueType;
typedef typename Superclass::StaticParameterValuesType StaticParameterValuesType;
typedef typename Superclass::StaticParameterMapType StaticParameterMapType;
typedef typename Superclass::IndexType IndexType;
itkSetMacro(FunctionString, mitk::ModelBase::FunctionStringType);
- /**@pre The Number of paremeters must be between 1 and 10.*/
+ /**@pre The Number of parameters must be between 1 and 10.*/
itkSetClampMacro(NumberOfParameters, ParametersSizeType, 1, 10);
mitk::ModelBase::FunctionStringType GetFunctionString() const override;
using Superclass::GenerateParameterizedModel;
ModelBasePointer GenerateParameterizedModel(const IndexType& currentPosition) const override;
StaticParameterMapType GetGlobalStaticParameters() const override;
ParametersType GetDefaultInitialParameterization() const override;
protected:
GenericParamModelParameterizer();
~GenericParamModelParameterizer() override;
mitk::ModelBase::FunctionStringType m_FunctionString;
/**Number of parameters the model should offer / the function string contains.*/
ParametersSizeType m_NumberOfParameters;
private:
//No copy constructor allowed
GenericParamModelParameterizer(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkIModelProvider.h b/Modules/ModelFit/include/mitkIModelProvider.h
index 3df525229c..9d248ff791 100644
--- a/Modules/ModelFit/include/mitkIModelProvider.h
+++ b/Modules/ModelFit/include/mitkIModelProvider.h
@@ -1,114 +1,114 @@
/*============================================================================
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 mitkIModelProvider_h
#define mitkIModelProvider_h
#include <mitkDataStorage.h>
#include <mitkIFileIO.h>
#include <mitkServiceInterface.h>
namespace mitk
{
class BaseData;
}
namespace itk
{
template <class T>
class SmartPointer;
}
namespace mitk
{
/**
* \ingroup MicroServices_Interfaces
*
* \brief The common interface for all providers of models for fitting in mitk.
*
* Implementations of this interface must be registered as a service
- * to make themselve available via the service registry. If the
+ * to make themselves available via the service registry. If the
* implementation is state-full, the service should be registered using
* a PrototypeServiceFactory.
*
* It is recommended to derive new implementations from ModelFitProviderBase,
* which provide correct service registration semantics.
*
* \sa ModelFitProviderBase
*/
struct MITKCORE_EXPORT IFileReader /** \cond */ : public IFileIO /** \endcond */
{
virtual ~IFileReader();
/**
* \brief Set the input location.
* \param location The file name to read from.
*/
virtual void SetInput(const std::string &location) = 0;
/**
* @brief Set an input stream to read from.
* @param location A custom label for the input stream.
* @param is The input stream.
*
* If \c is is \c NULL, this clears the current input stream and \c location
* is interpreted as a file-system path. Otherwise, \c location is a custom
* label describing the input stream \c is.
*/
virtual void SetInput(const std::string &location, std::istream *is) = 0;
/**
* @brief Get the current input location.
* @return The input location.
*/
virtual std::string GetInputLocation() const = 0;
/**
* @brief Get the input stream.
* @return The currently set input stream.
*/
virtual std::istream *GetInputStream() const = 0;
/**
* \brief Reads the specified file or input stream and returns its contents.
*
* \return A list of created BaseData objects.
*
* If GetInputStream() returns a non-null value, this method must use
* the returned stream object to read the data from. If no input stream
* was set, the data must be read from the path returned by GetInputLocation().
*
* \throws mitk::Exception
*/
virtual std::vector<itk::SmartPointer<BaseData>> Read() = 0;
/**
* \brief Reads the specified file or input stream, loading its
* contents into the provided DataStorage.
*
* \param ds The DataStorage to which the data is added.
* \return The set of added DataNodes to \c ds.
*
* This method may be overridden by implementations to create or
* reconstructed a hierarchy of mitk::DataNode instances in the
* provided mitk::DataStorage.
*
* \throws mitk::Exception
*/
virtual DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage &ds) = 0;
};
} // namespace mitk
MITK_DECLARE_SERVICE_INTERFACE(mitk::IFileReader, "org.mitk.IFileReader")
#endif
diff --git a/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h b/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h
index 6db669329f..8fb930fee7 100644
--- a/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h
+++ b/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h
@@ -1,104 +1,104 @@
/*============================================================================
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 mitkLevenbergMarquardtModelFitFunctor_h
#define mitkLevenbergMarquardtModelFitFunctor_h
#include <itkObject.h>
#include <itkLevenbergMarquardtOptimizer.h>
#include "mitkModelBase.h"
#include "mitkModelFitFunctorBase.h"
#include "mitkMVConstrainedCostFunctionDecorator.h"
#include "MitkModelFitExports.h"
namespace mitk
{
class MITKMODELFIT_EXPORT LevenbergMarquardtModelFitFunctor : public ModelFitFunctorBase
{
public:
typedef LevenbergMarquardtModelFitFunctor Self;
typedef ModelFitFunctorBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkNewMacro(Self);
itkTypeMacro(LevenbergMarquardtModelFitFunctor, ModelFitFunctorBase);
typedef Superclass::InputPixelArrayType InputPixelArrayType;
typedef Superclass::OutputPixelArrayType OutputPixelArrayType;
itkSetMacro(Epsilon, double);
itkSetMacro(GradientTolerance, double);
itkSetMacro(ValueTolerance, double);
itkSetMacro(DerivativeStepLength, double);
itkSetMacro(Iterations, unsigned int);
itkSetMacro(Scales, ::itk::LevenbergMarquardtOptimizer::ScalesType);
itkGetMacro(Epsilon, double);
itkGetMacro(GradientTolerance, double);
itkGetMacro(ValueTolerance, double);
itkGetMacro(DerivativeStepLength, double);
itkGetMacro(Iterations, unsigned int);
itkGetMacro(Scales, ::itk::LevenbergMarquardtOptimizer::ScalesType);
itkSetConstObjectMacro(ConstraintChecker, ConstraintCheckerBase);
itkGetConstObjectMacro(ConstraintChecker, ConstraintCheckerBase);
itkSetMacro(ActivateFailureThreshold, bool);
itkGetConstMacro(ActivateFailureThreshold, bool);
ParameterNamesType GetCriterionNames() const override;
protected:
typedef Superclass::ParametersType ParametersType;
typedef Superclass::SignalType SignalType;
LevenbergMarquardtModelFitFunctor();
~LevenbergMarquardtModelFitFunctor() override;
ParametersType DoModelFit(const SignalType& value, const ModelBase* model,
const ModelBase::ParametersType& initialParameters,
DebugParameterMapType& debugParameters) const override;
OutputPixelArrayType GetCriteria(const ModelBase* model, const ParametersType& parameters,
const SignalType& sample) const override;
/** Generator function that instantiates and parameterizes the cost function that should be used by the fit functor*/
virtual MVModelFitCostFunction::Pointer GenerateCostFunction(const SignalType& value,
const ModelBase* model) const;
ParameterNamesType DefineDebugParameterNames() const override;
private:
double m_Epsilon;
double m_GradientTolerance;
double m_ValueTolerance;
unsigned int m_Iterations;
double m_DerivativeStepLength;
::itk::LevenbergMarquardtOptimizer::ScalesType m_Scales;
/**Constraint checker. If set it will be used by the optimization strategies to add additional constraints to the
given cost function. */
ConstraintCheckerBase::ConstPointer m_ConstraintChecker;
- /**If set to true and an constraint checker is set. The cost function will allways fail if the penalty of the
+ /**If set to true and an constraint checker is set. The cost function will always fail if the penalty of the
checker reaches the threshold. In this case no function evaluation will be done-*/
bool m_ActivateFailureThreshold;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkMVModelFitCostFunction.h b/Modules/ModelFit/include/mitkMVModelFitCostFunction.h
index d6d5027c86..a2e4dab1f1 100644
--- a/Modules/ModelFit/include/mitkMVModelFitCostFunction.h
+++ b/Modules/ModelFit/include/mitkMVModelFitCostFunction.h
@@ -1,78 +1,78 @@
/*============================================================================
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 mitkMVModelFitCostFunction_h
#define mitkMVModelFitCostFunction_h
#include <itkMultipleValuedCostFunction.h>
#include <itkMacro.h>
#include "mitkModelFitCostFunctionInterface.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** Base class for all model fit cost function that return a multiple cost value
* It offers also a default implementation for the numerical computation of the
- * derivatives. Normaly you just have to (re)implement CalcMeasure().
+ * derivatives. Normally you just have to (re)implement CalcMeasure().
*/
class MITKMODELFIT_EXPORT MVModelFitCostFunction : public itk::MultipleValuedCostFunction, public ModelFitCostFunctionInterface
{
public:
typedef MVModelFitCostFunction Self;
typedef itk::MultipleValuedCostFunction Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef ModelFitCostFunctionInterface::SignalType SignalType;
typedef Superclass::MeasureType MeasureType;
typedef Superclass::DerivativeType DerivativeType;
void SetSample(const SignalType &sampleSet) override;
MeasureType GetValue(const ParametersType& parameter) const override;
void GetDerivative (const ParametersType &parameters, DerivativeType &derivative) const override;
unsigned int GetNumberOfValues (void) const override;
unsigned int GetNumberOfParameters (void) const override;
itkSetConstObjectMacro(Model, ModelBase);
itkGetConstObjectMacro(Model, ModelBase);
itkSetMacro(DerivativeStepLength, double);
itkGetConstMacro(DerivativeStepLength, double);
protected:
virtual MeasureType CalcMeasure(const ParametersType &parameters, const SignalType& signal) const = 0;
MVModelFitCostFunction() : m_DerivativeStepLength(1e-5)
{
}
~MVModelFitCostFunction() override{}
SignalType m_Sample;
private:
ModelBase::ConstPointer m_Model;
/**value (delta of parameters) used to compute the derivatives numerically*/
double m_DerivativeStepLength;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelBase.h b/Modules/ModelFit/include/mitkModelBase.h
index c68c14e1b3..339ed810d6 100644
--- a/Modules/ModelFit/include/mitkModelBase.h
+++ b/Modules/ModelFit/include/mitkModelBase.h
@@ -1,213 +1,213 @@
/*============================================================================
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 mitkModelBase_h
#define mitkModelBase_h
#include <iostream>
#include <itkArray.h>
#include <itkArray2D.h>
#include <itkObject.h>
#include "MitkModelFitExports.h"
#include "mitkModelTraitsInterface.h"
namespace mitk
{
/**@class ModelBase
* @brief Base class for (dynamic) models.
* A model can be used to calculate its signal given the discrete time grid of the signal
* and the parameters of the model.\n
* A model has 3 types of parameters:\n
* - parameters
* - static parameters
* - derived parameters
* .
* "Parameters" and "static parameters" are used to compute the signal of the model.
* "Parameters" are the ones that will be changed for/by model fitting.
* "Static parameters" are used to configure the model for fitting but are itself not
* part of the fitting scope (compare itk::Transform parameters and static parameters).
* "Derived parameters" are model specific parameters computed from "Parameters" e.g. (DerivedParam1 = Param1/Param2).
* It may be implemented if e.g. for practical usage not the fitted parameters are needed but
* derivation of them.
* @remark: If you implement your own model calls regard const correctness and do not change
* or undermine the constness of this base class. It is important because in case of fitting
* models are used in a multi threaded environment and must be thread safe. Thus the getter and
* computation functions are implemented as const and thread safe methods.*/
class MITKMODELFIT_EXPORT ModelBase : public itk::Object, public ModelTraitsInterface
{
public:
typedef ModelBase Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(ModelBase, itk::Object);
typedef ModelTraitsInterface::ModelResultType ModelResultType;
typedef ModelTraitsInterface::ParameterValueType ParameterValueType;
typedef ModelTraitsInterface::ParametersType ParametersType;
/** Type defining the time grid used be models.
* @remark the model time grid has a resolution in sec and not like the time geometry which uses ms.*/
typedef itk::Array<double> TimeGridType;
typedef ModelTraitsInterface::ParameterNameType ParameterNameType;
typedef ModelTraitsInterface::ParameterNamesType ParameterNamesType;
typedef ModelTraitsInterface::ParametersSizeType ParametersSizeType;
typedef ModelTraitsInterface::DerivedParameterNamesType DerivedParameterNamesType;
typedef ModelTraitsInterface::DerivedParametersSizeType DerivedParametersSizeType;
typedef double StaticParameterValueType;
typedef std::vector<StaticParameterValueType> StaticParameterValuesType;
typedef std::map<ParameterNameType, StaticParameterValuesType> StaticParameterMapType;
typedef double DerivedParameterValueType;
typedef std::map<ParameterNameType, DerivedParameterValueType> DerivedParameterMapType;
/**Default implementation returns a scale of 1.0 for every defined parameter.*/
ParamterScaleMapType GetParameterScales() const override;
/**Default implementation returns no unit string ("") for every defined parameter.*/
ParamterUnitMapType GetParameterUnits() const override;
/**Default implementation returns a scale of 1.0 for every defined derived parameter.*/
DerivedParamterScaleMapType GetDerivedParameterScales() const override;
/**Default implementation returns no unit string ("") for every defined derived parameter.*/
DerivedParamterUnitMapType GetDerivedParameterUnits() const override;
/**Default implementation returns GetClassID as display name.*/
std::string GetModelDisplayName() const override;
/**Default implementation returns "Unkown" as model type.*/
std::string GetModelType() const override;
/**Default implementation returns an empty functions string.*/
FunctionStringType GetFunctionString() const override;
/**Default implementation the class name of the concrete instance as ID.*/
ModellClassIDType GetClassID() const override;
/**Default implementation returns an empty string.*/
std::string GetXName() const override;
/**Default implementation returns an empty string.*/
std::string GetXAxisName() const override;
/**Default implementation returns an empty string.*/
std::string GetXAxisUnit() const override;
/**Default implementation returns an empty string.*/
std::string GetYAxisName() const override;
/**Default implementation returns an empty string.*/
std::string GetYAxisUnit() const override;
/** Returns the names of static parameters that will be used when using
* the model to compute the signal (but are not defined via GetSignal()).*/
virtual ParameterNamesType GetStaticParameterNames() const = 0;
/** Returns the number of static parameters that will be used when using
* the model to compute the signal (but are not defined via GetSignal()).*/
virtual ParametersSizeType GetNumberOfStaticParameters() const = 0;
/**Default implementation returns no unit string ("") for every defined parameter.*/
virtual ParamterUnitMapType GetStaticParameterUnits() const;
/** Returns the names of derived parameters that can/will be computed by the model
* given specific model parameters.
* @remark Default implementation has no derived parameters*/
DerivedParameterNamesType GetDerivedParameterNames() const override;
/** Returns the number of derived parameters that can/will be computed by the model
* given specific model parameters.
* @remark Default implementation has no derived parameters*/
DerivedParametersSizeType GetNumberOfDerivedParameters() const override;
/** Generic interface method that can be used to set the static parameters of the model
* before it is used.
* It checks the validity of the passed map and uses SetStaticParameter to set the values.
* @param parameters The map with the static parameters and their values.
* @param allParameters If true an exception will be thrown if the keys of passed parameters do
* not equal the return of GetStaticParameterNames. Thus if true, one must set all static
* parameters of the model.
* @pre Parameters must only contain keys that exist in GetStaticParameterNames()
* @pre If allParameters == true, parameters must define all keys of GetStaticParameterNames()*/
void SetStaticParameters(const StaticParameterMapType& parameters, bool allParameters = true);
/** Generic interface method that can be used to retrieve the static parameters of the model;
* e.g. in order to serialize the model settings.
* It calls GetStaticParameter for every name defined in GetStaticParameterNames().*/
StaticParameterMapType GetStaticParameters() const;
/** Generic interface method that computes all derived parameters implemented for the given models.
* To changed the derived parameter computation. ComputeDerivedParameters must be (re)implemented.
* @pre parameters must have the right size.
* @param parameters The parameters of the model for which the derived parameters should be computed.
* It calls GetStaticParameter for every name defined in GetStaticParameterNames().
* @remark Default implementation has no derived parameters*/
DerivedParameterMapType GetDerivedParameters(const ParametersType& parameters) const;
- /** Sets the time grid of the model. It indicates the time points correlated with the signal the modell should
+ /** Sets the time grid of the model. It indicates the time points correlated with the signal the model should
produce.
@remark The resolution of the time grid is in seconds. (Not in ms like the mitk::TimeGeometry)*/
virtual void SetTimeGrid(const TimeGridType& grid);
- /** Gets the time grid of the model. It indicates the time points correlated with the signal the modell should
+ /** Gets the time grid of the model. It indicates the time points correlated with the signal the model should
produce.
@remark The resolution of the time grid is in seconds. (Not in ms like the mitk::TimeGeometry)*/
itkGetConstReferenceMacro(TimeGrid, TimeGridType);
ModelResultType GetSignal(const ParametersType& parameters) const;
protected:
virtual ModelResultType ComputeModelfunction(const ParametersType& parameters) const = 0;
/** Member is called by GetSignal() before ComputeModelfunction(). It indicates if model is in a valid state and
* ready to compute the signal. The default implementation checks nothing and always returns true.
* Reimplement to realize special behavior for derived classes.
* @param [out] error Set internally to indicate the error reason if method returns false. Is used by GetSignal() for the
* exception comment.
* @return Returns true if the model is valid and can compute a signal. Otherwise it returns false.*/
virtual bool ValidateModel(std::string& error) const;
/** Helper function called by GetDerivedParameters(). Implement in derived classes to realize
* the concrete computation of derived parameters.
* @remark Default implementation has no derived parameters*/
virtual DerivedParameterMapType ComputeDerivedParameters(const ParametersType& parameters) const;
/** Helper function called by SetStaticParameters(). Implement in derived classes to realize
* the concrete setting of static parameters.*/
virtual void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) = 0;
/** Helper function called by GetStaticParameters(). Implement in derived classes to realize
* the concrete retrieval of static parameters.*/
virtual StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const = 0;
ModelBase();
~ModelBase() override;
void PrintSelf(std::ostream& os, ::itk::Indent indent) const override;
//timeGrid in seconds
TimeGridType m_TimeGrid;
private:
//No copy constructor allowed
ModelBase(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h b/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h
index 4a9ddec0ae..030a64c434 100644
--- a/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h
+++ b/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h
@@ -1,81 +1,81 @@
/*============================================================================
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 mitkModelFitInfoSignalGenerationFunctor_h
#define mitkModelFitInfoSignalGenerationFunctor_h
#include "mitkModelBasedValueFunctorBase.h"
#include "mitkModelParameterizerBase.h"
#include "mitkModelFitInfo.h"
#include "mitkModelFitParameterValueExtraction.h"
#include <MitkModelFitExports.h>
namespace mitk
{
/** Functor class that can be used to generate a model signal for each index.
This class is similar to ModelDataGenerationFunctor. But instead of using
the passed input value as model parameters in the compute function, the model
fit info is used to deduce the parameters based on the index.
The time grid and the parameterized model are provided by the model
parameterizer. This this functor will generate a signal for each index position
and return the signal as output.*/
class MITKMODELFIT_EXPORT ModelFitInfoSignalGenerationFunctor : public ModelBasedValueFunctorBase
{
public:
typedef ModelFitInfoSignalGenerationFunctor Self;
typedef ModelBasedValueFunctorBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkFactorylessNewMacro(Self);
itkTypeMacro(ModelFitInfoSignalGenerationFunctor, ModelBasedValueFunctorBase);
typedef std::vector<std::string> ParameterNamesType;
typedef ModelBase::ModelResultType SignalType;
typedef itk::Array<ModelBase::ParameterValueType> ModelParametersType;
itkSetConstObjectMacro(ModelParameterizer, ModelParameterizerBase);
itkGetConstObjectMacro(ModelParameterizer, ModelParameterizerBase);
itkSetConstObjectMacro(FitInfo, mitk::modelFit::ModelFitInfo);
itkGetConstObjectMacro(FitInfo, mitk::modelFit::ModelFitInfo);
IndexedValueFunctorBase::OutputPixelVectorType Compute(const InputPixelVectorType & value, const IndexType& currentIndex) const override;
- /** Convinient overload because this functor does not need the value */
+ /** Convenient overload because this functor does not need the value */
virtual IndexedValueFunctorBase::OutputPixelVectorType Compute(const IndexType& currentIndex) const;
unsigned int GetNumberOfOutputs() const override;
GridArrayType GetGrid() const override;
protected:
/**Method is called by Compute() to specify the parameters used to generate the model signal for the current index.
The default implementation just extracts the parameters out of the model fit info and maps it into the result vector.
Reimplement the method to change this behavior.*/
virtual ModelBase::ParametersType CompileModelParameters(const IndexType& currentIndex, const ModelBase * model) const;
ModelFitInfoSignalGenerationFunctor();
~ModelFitInfoSignalGenerationFunctor() override;
private:
ModelParameterizerBase::ConstPointer m_ModelParameterizer;
modelFit::ModelFitInfo::ConstPointer m_FitInfo;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h
index 4ee68c1612..b99fb94918 100644
--- a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h
+++ b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h
@@ -1,167 +1,167 @@
/*============================================================================
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 mitkModelFitPlotDataHelper_h
#define mitkModelFitPlotDataHelper_h
#include <vector>
#include "mitkPoint.h"
#include "mitkPointSet.h"
#include "mitkModelBase.h"
#include "mitkWeakPointer.h"
#include "mitkCommon.h"
#include "mitkModelFitInfo.h"
#include "itkMapContainer.h"
#include "MitkModelFitExports.h"
namespace mitk
{
class ModelParameterizerBase;
class Image;
using PlotDataValues = std::vector<std::pair<double, double>>;
/** Simple helper structure that represents a curve for plots and its generation time*/
class MITKMODELFIT_EXPORT PlotDataCurve : public ::itk::Object
{
public:
mitkClassMacroItkParent(PlotDataCurve, itk::Object);
itkFactorylessNewMacro(Self);
using ValuesType = PlotDataValues;
virtual void SetValues(const ValuesType& _arg);
virtual void SetValues(ValuesType&& _arg);
itkGetConstReferenceMacro(Values, ValuesType);
itkGetMacro(Values, ValuesType);
PlotDataCurve& operator=(const PlotDataCurve& rhs);
PlotDataCurve& operator=(PlotDataCurve&& rhs) noexcept;
void Reset();
protected:
PlotDataCurve();
~PlotDataCurve() override = default;
private:
/** values of the curve */
ValuesType m_Values;
PlotDataCurve(const PlotDataCurve& other) = delete;
};
/** Collection of plot curves, e.g. every plot curve for a certain world coordinate position*/
using PlotDataCurveCollection = itk::MapContainer<std::string, PlotDataCurve::Pointer>;
/** Structure containing all information for one model fit */
struct MITKMODELFIT_EXPORT ModelFitPlotData
{
/** Plots that are related to the world coordinate labeled as current position.*/
PlotDataCurveCollection::Pointer currentPositionPlots;
using PositionalCurveCollection = std::pair<mitk::Point3D, PlotDataCurveCollection::Pointer>;
using PositionalCollectionMap = std::map<mitk::PointSet::PointIdentifier, PositionalCurveCollection>;
/** Plot collections that are related to specific world coordinates (inspection position bookmarks).*/
PositionalCollectionMap positionalPlots;
/** Plot collection for static plots of the fit that do not depend on some coordinates. */
PlotDataCurveCollection::Pointer staticPlots;
/** Pointer to the model fit that correspondens with this plot data.*/
mitk::modelFit::ModelFitInfo::Pointer fitInfo;
/** Helper function to get the collection of the current position.
Returns nullptr if no current position exists.*/
static const PlotDataCurve* GetSamplePlot(const PlotDataCurveCollection* coll);
static const PlotDataCurve* GetSignalPlot(const PlotDataCurveCollection* coll);
static const PlotDataCurve* GetInterpolatedSignalPlot(const PlotDataCurveCollection* coll);
/** Helper function that generates a humand readable name for the passed value of a positional collection map.*/
static std::string GetPositionalCollectionName(const PositionalCollectionMap::value_type& mapValue);
const PlotDataCurveCollection* GetPositionalPlot(const mitk::Point3D& point) const;
const PlotDataCurveCollection* GetPositionalPlot(mitk::PointSet::PointIdentifier id) const;
/**returns the minimum (first element) and maximum (second element) of x of all plot data*/
PlotDataValues::value_type GetXMinMax() const;
/**returns the minimum (first element) and maximum (second element) of y of all plot data*/
PlotDataValues::value_type GetYMinMax() const;
ModelFitPlotData();
};
/** Helper function that actualizes min and max by the y values given in data.*/
void CheckYMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max);
/** Helper function that actualizes min and max by the x values given in data.*/
void CheckXMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max);
- /** Function generates curve data for the signal defined by the passed informations.
+ /** Function generates curve data for the signal defined by the passed information.
@param position The position in world coordinates the curve should be generated for.
@param fitInfo Pointer to the fit info that defines the model/fit that produces the signal.
@param timeGrid Defines the time grid of the generated signal.
@param parameterizer Pointer to a parameterizer instance that is used to configure the model to generate the signal.
If pointer is not set. The default parameterizer based on the fitInfo instance will be used.
@pre position must be within the model fit input image
@pre fitInfo must be a valid pointer.
*/
MITKMODELFIT_EXPORT PlotDataCurve::Pointer
GenerateModelSignalPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer = nullptr);
- /** Function generates curve data for all additinal inputs (e.g. ROI signal, AIF)
+ /** Function generates curve data for all additional inputs (e.g. ROI signal, AIF)
stored in the fit information. The keys in the map are the same keys like in the fitInfo.
@param position The position in world coordinates the curve should be generated for.
@param fitInfo Pointer to the fit info that defines the model/fit that produces the signal.
@param timeGrid Defines the time grid of the generated signal.
@pre position must be within the model fit input image
@pre fitInfo must be a valid pointer.
*/
MITKMODELFIT_EXPORT PlotDataCurveCollection::Pointer
GenerateAdditionalModelFitPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid);
/** Function generates curve data for a given image instance.
@param position The position in world coordinates the curve should be generated for.
@param image Pointer to the image the signal should be extracted from.
@param timeGrid Defines the time grid of the generated signal.
@pre position must be within the model fit input image
@pre image must be a valid pointer.
@pre image time steps must equal the timeGrid size.
*/
MITKMODELFIT_EXPORT PlotDataCurve::Pointer
GenerateImageSamplePlotData(const mitk::Point3D& position, const mitk::Image* image, const mitk::ModelBase::TimeGridType& timeGrid);
/**
* Keyword used in curve collections as key for the sample (extracted from an image) plot.
*/
MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_SAMPLE_NAME();
/**
* Keyword used in curve collections as key for the signal (generated by a model) plot.
*/
MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_SIGNAL_NAME();
/**
* Keyword used in curve collections as key for the interpolated (hires) signal (generated by a model) plot.
*/
MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME();
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitResultHelper.h b/Modules/ModelFit/include/mitkModelFitResultHelper.h
index 58bb0bc186..1190754d2c 100644
--- a/Modules/ModelFit/include/mitkModelFitResultHelper.h
+++ b/Modules/ModelFit/include/mitkModelFitResultHelper.h
@@ -1,61 +1,61 @@
/*============================================================================
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 mitkModelFitResultHelper_h
#define mitkModelFitResultHelper_h
#include <mitkImage.h>
#include <mitkDataNode.h>
#include "mitkScalarListLookupTableProperty.h"
#include "mitkModelBase.h"
#include "mitkModelFitParameter.h"
#include "mitkModelFitStaticParameterMap.h"
#include "MitkModelFitExports.h"
namespace mitk
{
class DataStorage;
class ModelTraitsInterface;
class ModelBase;
namespace modelFit
{
class ModelFitInfo;
typedef std::map<ModelBase::ParameterNameType,Image::Pointer> ModelFitResultImageMapType;
typedef std::vector<DataNode::Pointer> ModelFitResultNodeVectorType;
/**Helper function that sets the properties of the passed base data according to the given model fit info instance and parameter specification.
@param data Instance that properties should be configured.
@param name Name of the parameter this data instance represents.
@param dataType Type of the parameter this data instance represents.
- @param fitInfo Instance to the fit info that containes the information of the fit that derived the parameter.
+ @param fitInfo Instance to the fit info that contains the information of the fit that derived the parameter.
@pre Data must point to a valid instance.
@pre fitInfo must point to an valid instance.
*/
MITKMODELFIT_EXPORT void SetModelFitDataProperties(mitk::BaseData* data, const ModelBase::ParameterNameType& name, modelFit::Parameter::Type dataType, const modelFit::ModelFitInfo* fitInfo);
MITKMODELFIT_EXPORT mitk::ScalarListLookupTableProperty::Pointer ConvertStaticParametersToProperty(const mitk::modelFit::StaticParameterMap& params);
MITKMODELFIT_EXPORT DataNode::Pointer CreateResultNode(const ModelBase::ParameterNameType& name, modelFit::Parameter::Type nodeType, Image* parameterImage, const ModelFitInfo* modelFitInfo);
MITKMODELFIT_EXPORT ModelFitResultNodeVectorType CreateResultNodeMap(const ModelFitResultImageMapType& results, const ModelFitResultImageMapType& derivedResults, const ModelFitResultImageMapType& criterionResults, const ModelFitResultImageMapType& evaluationResults, const ModelFitInfo* fitInfo);
MITKMODELFIT_EXPORT void StoreResultsInDataStorage(DataStorage* storage, const ModelFitResultNodeVectorType& resultNodes, DataNode* parentNode = nullptr);
}
}
#endif
diff --git a/Modules/ModelFit/include/mitkSVModelFitCostFunction.h b/Modules/ModelFit/include/mitkSVModelFitCostFunction.h
index 0e520593f4..acff805330 100644
--- a/Modules/ModelFit/include/mitkSVModelFitCostFunction.h
+++ b/Modules/ModelFit/include/mitkSVModelFitCostFunction.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkSVModelFitCostFunction_h
#define mitkSVModelFitCostFunction_h
#include <itkSingleValuedCostFunction.h>
#include <itkMacro.h>
#include "mitkModelFitCostFunctionInterface.h"
#include "MitkModelFitExports.h"
namespace mitk
{
-/** Base class for all model fit cost function that return a singel cost value*/
+/** Base class for all model fit cost function that return a single cost value*/
class MITKMODELFIT_EXPORT SVModelFitCostFunction : public itk::SingleValuedCostFunction, public ModelFitCostFunctionInterface
{
public:
typedef SVModelFitCostFunction Self;
typedef itk::SingleValuedCostFunction Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef ModelFitCostFunctionInterface::SignalType SignalType;
typedef Superclass::MeasureType MeasureType;
typedef Superclass::DerivativeType DerivativeType;
void SetSample(const SignalType &sampleSet) override;
MeasureType GetValue(const ParametersType& parameter) const override;
void GetDerivative (const ParametersType &parameters, DerivativeType &derivative) const override;
unsigned int GetNumberOfParameters (void) const override;
itkSetConstObjectMacro(Model, ModelBase);
itkGetConstObjectMacro(Model, ModelBase);
itkSetMacro(DerivativeStepLength, double);
itkGetConstMacro(DerivativeStepLength, double);
protected:
virtual MeasureType CalcMeasure(const ParametersType &parameters, const SignalType& signal) const = 0;
SVModelFitCostFunction(): m_DerivativeStepLength(1e-5)
{
}
~SVModelFitCostFunction() override{}
SignalType m_Sample;
private:
ModelBase::ConstPointer m_Model;
/**value (delta of parameters) used to compute the derivatives numerically*/
double m_DerivativeStepLength;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkTimeGridHelper.h b/Modules/ModelFit/include/mitkTimeGridHelper.h
index 3993bb3a20..4579dfc5bd 100644
--- a/Modules/ModelFit/include/mitkTimeGridHelper.h
+++ b/Modules/ModelFit/include/mitkTimeGridHelper.h
@@ -1,37 +1,37 @@
/*============================================================================
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 mitkTimeGridHelper_h
#define mitkTimeGridHelper_h
#include "mitkModelBase.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/* Checks if the time grid is monotone increasing (timeGrid[n] <= timeGrid[n+1]).
* It is a precondition for the helper interpolate time grid.*/
MITKMODELFIT_EXPORT bool TimeGridIsMonotonIncreasing(const ModelBase::TimeGridType timeGrid);
/* Helper function that interpolates a passed signal to a new time grid.
* @pre The time grids must be monotone increasing. Use TimeGridIsMonotonIncreasing() to verify that.*/
MITKMODELFIT_EXPORT ModelBase::ModelResultType InterpolateSignalToNewTimeGrid(const ModelBase::ModelResultType& inputSignal, const ModelBase::TimeGridType& inputGrid, const ModelBase::TimeGridType& outputGrid);
/** Super sample passed time grid by a given supersampling rate and interpolates linear in between original time steps.
* @param grid
- * @param samplingRate Defines how many samples should be generated between to original time steps (including the preceeding time step). E.g. a sampling rate of 1 will just returns the original grid untouched;
- a sampling rate of 3 will generate to aditional steps between to original steps.*/
+ * @param samplingRate Defines how many samples should be generated between to original time steps (including the preceding time step). E.g. a sampling rate of 1 will just returns the original grid untouched;
+ a sampling rate of 3 will generate to additional steps between to original steps.*/
MITKMODELFIT_EXPORT ModelBase::TimeGridType GenerateSupersampledTimeGrid(const mitk::ModelBase::TimeGridType& grid, const unsigned int samplingRate);
}
#endif
diff --git a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp
index 1467eabd61..0654f2220e 100644
--- a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp
+++ b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.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.
============================================================================*/
#include "mitkModelFitFunctorBase.h"
mitk::ModelFitFunctorBase::OutputPixelArrayType
mitk::ModelFitFunctorBase::
Compute(const InputPixelArrayType& value, const ModelBase* model,
const ModelBase::ParametersType& initialParameters) const
{
if (!model)
{
itkExceptionMacro("Cannot compute fit. Passed model is not defined.");
}
if (model->GetNumberOfParameters() != initialParameters.Size())
{
itkExceptionMacro("Cannot compute fit. Parameter count of passed model and passed initial parameters differ. Model parameter count: "
<< model->GetNumberOfParameters() << "; Initial parameters: " << initialParameters);
}
SignalType sample(value.size());
for (SignalType::SizeValueType i = 0; i < sample.Size(); ++i)
{
sample[i] = value [i];
}
DebugParameterMapType debugParams;
ParameterNamesType debugNames;
if (this->m_DebugParameterMaps)
{
debugNames = this->GetDebugParameterNames();
}
ParametersType fittedParameters = DoModelFit(sample, model, initialParameters, debugParams);
OutputPixelArrayType derivedParameters = this->GetDerivedParameters(model, fittedParameters);
OutputPixelArrayType criteria = this->GetCriteria(model, fittedParameters, sample);
OutputPixelArrayType evaluationParameters = this->GetEvaluationParameters(model, fittedParameters,
sample);
if (criteria.size() != this->GetCriterionNames().size())
{
- itkExceptionMacro("ModelFitInfo implementation seems to be inconsitent. Number of criterion values is not equal to number of criterion names.");
+ itkExceptionMacro("ModelFitInfo implementation seems to be inconsistent. Number of criterion values is not equal to number of criterion names.");
}
OutputPixelArrayType result(fittedParameters.Size() + derivedParameters.size() + criteria.size() +
evaluationParameters.size() + debugNames.size());
for (ParametersType::SizeValueType i = 0; i < fittedParameters.Size(); ++i)
{
result[i] = fittedParameters[i];
}
OutputPixelArrayType::size_type offset = fittedParameters.Size();
for (OutputPixelArrayType::size_type j = 0; j < derivedParameters.size(); ++j)
{
result[offset + j] = derivedParameters[j];
}
offset += derivedParameters.size();
for (OutputPixelArrayType::size_type j = 0; j < criteria.size(); ++j)
{
result[offset + j] = criteria[j];
}
offset += criteria.size();
for (OutputPixelArrayType::size_type j = 0; j < evaluationParameters.size(); ++j)
{
result[offset + j] = evaluationParameters[j];
}
offset += evaluationParameters.size();
for (OutputPixelArrayType::size_type j = 0; j < debugNames.size(); ++j)
{
DebugParameterMapType::const_iterator pos = debugParams.find(debugNames[j]);
if (pos == debugParams.end())
{
- itkExceptionMacro("ModelFitInfo implementation seems to be inconsitent. Debug parameter defined by functor is not in its returned debug map. Invalid debug parameter name: "<<debugNames[j]);
+ itkExceptionMacro("ModelFitInfo implementation seems to be inconsistent. Debug parameter defined by functor is not in its returned debug map. Invalid debug parameter name: "<<debugNames[j]);
}
else
{
result[offset + j] = pos->second;
}
}
return result;
};
unsigned int
mitk::ModelFitFunctorBase::GetNumberOfOutputs(const ModelBase* model) const
{
if (!model)
{
itkExceptionMacro("Cannot get number of outputs. Model is not defined.");
}
return model->GetNumberOfParameters() + model->GetNumberOfDerivedParameters() +
this->GetCriterionNames().size() + m_CostFunctionMap.size()+ this->GetDebugParameterNames().size();
};
void
mitk::ModelFitFunctorBase::ResetEvaluationParameters()
{
m_Mutex.lock();
m_CostFunctionMap.clear();
m_Mutex.unlock();
};
void
mitk::ModelFitFunctorBase::RegisterEvaluationParameter(const std::string& parameterName,
SVModelFitCostFunction* evaluationCostFunction)
{
m_Mutex.lock();
SVModelFitCostFunction::Pointer costFunctPtr = evaluationCostFunction;
m_CostFunctionMap.insert(std::make_pair(parameterName, costFunctPtr));
m_Mutex.unlock();
};
mitk::ModelFitFunctorBase::ParameterNamesType
mitk::ModelFitFunctorBase::GetEvaluationParameterNames() const
{
m_Mutex.lock();
ParameterNamesType result;
for (CostFunctionMapType::const_iterator pos = m_CostFunctionMap.begin();
pos != m_CostFunctionMap.end(); ++pos)
{
result.push_back(pos->first);
}
m_Mutex.unlock();
return result;
};
const mitk::SVModelFitCostFunction*
mitk::ModelFitFunctorBase::GetEvaluationParameterCostFunction(const std::string& parameterName)
const
{
const SVModelFitCostFunction* result = nullptr;
m_Mutex.lock();
CostFunctionMapType::const_iterator pos = m_CostFunctionMap.find(parameterName);
if (pos != m_CostFunctionMap.end())
{
result = (pos->second).GetPointer();
}
m_Mutex.unlock();
return result;
};
mitk::ModelFitFunctorBase::ParameterNamesType
mitk::ModelFitFunctorBase::GetDebugParameterNames() const
{
ParameterNamesType result;
if (this->m_DebugParameterMaps)
{
result = this->DefineDebugParameterNames();
}
return result;
};
mitk::ModelFitFunctorBase::
ModelFitFunctorBase() : m_DebugParameterMaps(false)
{};
mitk::ModelFitFunctorBase::
~ModelFitFunctorBase() {};
mitk::ModelFitFunctorBase::OutputPixelArrayType
mitk::ModelFitFunctorBase::GetDerivedParameters(const ModelBase* model,
const ParametersType& parameters) const
{
ModelBase::DerivedParameterMapType derivedParameterMap = model->GetDerivedParameters(parameters);
OutputPixelArrayType result(derivedParameterMap.size());
unsigned int i = 0;
for (ModelBase::DerivedParameterMapType::const_iterator pos = derivedParameterMap.begin();
pos != derivedParameterMap.end(); ++pos, ++i)
{
result[i] = pos->second;
}
return result;
};
mitk::ModelFitFunctorBase::OutputPixelArrayType
mitk::ModelFitFunctorBase::GetEvaluationParameters(const ModelBase* model,
const ParametersType& parameters, const SignalType& sample) const
{
m_Mutex.lock();
OutputPixelArrayType result(m_CostFunctionMap.size());
unsigned int i = 0;
for (CostFunctionMapType::const_iterator pos = m_CostFunctionMap.begin();
pos != m_CostFunctionMap.end(); ++pos, ++i)
{
//break constness to configure evaluation cost functions. This operatoin is guarded be the mutex
//after costFct->GetValue() the cost function may change its state again and is irrelevant for the
//current call of GetEvaluationParameters
SVModelFitCostFunction* costFct = const_cast<SVModelFitCostFunction*>(pos->second.GetPointer());
costFct->SetModel(model);
costFct->SetSample(sample);
result[i] = costFct->GetValue(parameters);
}
m_Mutex.unlock();
return result;
};
diff --git a/Modules/ModelFit/src/Models/mitkModelBase.cpp b/Modules/ModelFit/src/Models/mitkModelBase.cpp
index df8d2ebadb..91689db33c 100644
--- a/Modules/ModelFit/src/Models/mitkModelBase.cpp
+++ b/Modules/ModelFit/src/Models/mitkModelBase.cpp
@@ -1,266 +1,266 @@
/*============================================================================
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 "mitkModelBase.h"
#include "itkMacro.h"
#include <algorithm>
mitk::ModelBase::ParamterScaleMapType
mitk::ModelBase::GetParameterScales() const
{
ParamterScaleMapType result;
ParameterNamesType names = GetParameterNames();
for (ParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, 1.0));
}
return result;
};
mitk::ModelBase::ParamterUnitMapType
mitk::ModelBase::GetParameterUnits() const
{
ParamterUnitMapType result;
ParameterNamesType names = GetParameterNames();
for (ParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, ""));
}
return result;
};
mitk::ModelBase::DerivedParamterScaleMapType
mitk::ModelBase::GetDerivedParameterScales() const
{
DerivedParamterScaleMapType result;
DerivedParameterNamesType names = this->GetDerivedParameterNames();
for (DerivedParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, 1.0));
}
return result;
};
mitk::ModelBase::DerivedParamterUnitMapType
mitk::ModelBase::GetDerivedParameterUnits() const
{
DerivedParamterUnitMapType result;
DerivedParameterNamesType names = this->GetDerivedParameterNames();
for (DerivedParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, ""));
}
return result;
};
std::string
mitk::ModelBase::GetModelDisplayName() const
{
return this->GetClassID();
};
std::string mitk::ModelBase::GetModelType() const
{
return "Unkown";
};
mitk::ModelBase::FunctionStringType mitk::ModelBase::GetFunctionString() const
{
return "";
};
mitk::ModelBase::ModellClassIDType mitk::ModelBase::GetClassID() const
{
return this->GetNameOfClass();
};
std::string mitk::ModelBase::GetXName() const
{
return "";
};
std::string mitk::ModelBase::GetXAxisName() const
{
return "";
};
std::string mitk::ModelBase::GetXAxisUnit() const
{
return "";
};
std::string mitk::ModelBase::GetYAxisName() const
{
return "";
};
std::string mitk::ModelBase::GetYAxisUnit() const
{
return "";
}
mitk::ModelBase::ModelBase()
{
}
mitk::ModelBase::~ModelBase()
{
}
mitk::ModelBase::ModelResultType mitk::ModelBase::GetSignal(const ParametersType& parameters) const
{
if (parameters.size() != this->GetNumberOfParameters())
{
itkExceptionMacro("Passed parameter set has wrong size for model. Cannot evaluate model. Required size: "
<< this->GetNumberOfParameters() << "; passed parameters: " << parameters);
}
std::string error;
if (!ValidateModel(error))
{
itkExceptionMacro("Cannot evaluate model and return signal. Model is in an invalid state. Validation error: "
<< error);
}
ModelResultType signal = ComputeModelfunction(parameters);
return signal;
}
bool mitk::ModelBase::ValidateModel(std::string& /*error*/) const
{
return true;
};
void mitk::ModelBase::SetTimeGrid(const TimeGridType& grid)
{
itkDebugMacro("setting TimeGrid to " << grid);
if (grid.GetSize() == 0)
{
itkExceptionMacro("Time Grid Vector is empty! Set valid time grid");
}
if (this->m_TimeGrid != grid)
{
this->m_TimeGrid = grid;
this->Modified();
}
}
void mitk::ModelBase::PrintSelf(std::ostream& os, ::itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Time grid: " << m_TimeGrid;
};
void mitk::ModelBase::SetStaticParameters(const StaticParameterMapType& parameters,
bool allParameters)
{
ParameterNamesType names = this->GetStaticParameterNames();
if ((parameters.size() != names.size()) && allParameters)
{
itkExceptionMacro("Cannot set static parameter of model. Passed parameters does not define all parameters correctly. Required size:"
<< names.size() << "; passed size: " << parameters.size());
}
for (StaticParameterMapType::const_iterator pos = parameters.begin(); pos != parameters.end();
++pos)
{
ParameterNamesType::iterator finding = std::find(names.begin(), names.end(), pos->first);
if (finding == names.end())
{
itkExceptionMacro("Cannot set static parameter of model. Passed parameter name is not in the list of valid names. Passed name: "
<< pos->first);
}
}
//Setting is done in an other loop to ensure that the state of the model only changes if all values are valid.
for (StaticParameterMapType::const_iterator pos = parameters.begin(); pos != parameters.end();
++pos)
{
this->SetStaticParameter(pos->first, pos->second);
}
};
mitk::ModelBase::StaticParameterMapType mitk::ModelBase::GetStaticParameters() const
{
StaticParameterMapType result;
ParameterNamesType names = this->GetStaticParameterNames();
for (ParameterNamesType::const_iterator pos = names.begin(); pos != names.end(); ++pos)
{
StaticParameterValuesType values = this->GetStaticParameterValue(*pos);
result.insert(std::make_pair(*pos, values));
}
return result;
};
mitk::ModelBase::DerivedParameterNamesType mitk::ModelBase::GetDerivedParameterNames() const
{
ParameterNamesType emptyResult;
return emptyResult;
};
mitk::ModelBase::ParamterUnitMapType
mitk::ModelBase::GetStaticParameterUnits() const
{
ParamterUnitMapType result;
ParameterNamesType names = GetStaticParameterNames();
for (ParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, ""));
}
return result;
};
mitk::ModelBase::DerivedParametersSizeType mitk::ModelBase::GetNumberOfDerivedParameters() const
{
return 0;
};
mitk::ModelBase::DerivedParameterMapType mitk::ModelBase::GetDerivedParameters(
const mitk::ModelBase::ParametersType& parameters) const
{
if (parameters.size() != this->GetNumberOfParameters())
{
- itkExceptionMacro("Cannot compute derived parametes. Passed parameters does not define all parameters correctly. Required size:"
+ itkExceptionMacro("Cannot compute derived parameters. Passed parameters does not define all parameters correctly. Required size:"
<< this->GetNumberOfParameters() << "; passed size: " << parameters.size());
}
return ComputeDerivedParameters(parameters);
};
mitk::ModelBase::DerivedParameterMapType mitk::ModelBase::ComputeDerivedParameters(
const mitk::ModelBase::ParametersType& /*parameters*/) const
{
DerivedParameterMapType emptyResult;
return emptyResult;
};
diff --git a/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp b/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp
index 6ec5cd98dc..3741f05822 100644
--- a/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp
+++ b/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp
@@ -1,321 +1,321 @@
/*============================================================================
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 "mitkThreeStepLinearModel.h"
#include <mitkIOUtil.h>
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_y_bl = "baseline";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_x0 = "x_changepoint1";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_x1 = "x_changepoint2";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_b0 = "slope1";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_b1 = "slope2";
const unsigned int mitk::ThreeStepLinearModel::NUMBER_OF_PARAMETERS = 5;
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_y_bl = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_x0 = "[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_x1 = "[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_b0 = "[unit of y]/[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_b1 = "[unit of y]/[unit of x]";
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_y_bl = 0;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_x0 = 1;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_x1 = 2;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_b0 = 3;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_b1 = 4;
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_auc = "auc";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_x_fin = "x_final";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y_fin = "y_final";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y_max = "y_max";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y1 = "y-intercept1";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y2 = "y-intercept2";
const unsigned int mitk::ThreeStepLinearModel::NUMBER_OF_DERIVED_PARAMETERS = 6;
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_auc = "[unit of x]*[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_x_fin = "[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y_fin = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y_max = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y1 = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y2 = "[unit of y]";
const unsigned int mitk::ThreeStepLinearModel::NUMBER_OF_STATIC_PARAMETERS = 0;
const std::string mitk::ThreeStepLinearModel::MODEL_DISPLAY_NAME = "Three Step Linear Model";
const std::string mitk::ThreeStepLinearModel::MODEL_TYPE = "Generic";
const std::string mitk::ThreeStepLinearModel::FUNCTION_STRING = "if x < x_changepoint1: y(x) = baseline, else if x_changepoint1 <= x <= x_changepoint2: y(x) = y-intercept1 + slope1*x, else if x>x_changepoint2: y(x) = y-intercept2 + slope2*x";
const std::string mitk::ThreeStepLinearModel::X_NAME = "x";
const std::string mitk::ThreeStepLinearModel::X_AXIS_NAME = "X";
const std::string mitk::ThreeStepLinearModel::X_AXIS_UNIT = "unit of x";
const std::string mitk::ThreeStepLinearModel::Y_AXIS_NAME = "Y";
const std::string mitk::ThreeStepLinearModel::Y_AXIS_UNIT = "unit of y";
std::string mitk::ThreeStepLinearModel::GetModelDisplayName() const
{
return MODEL_DISPLAY_NAME;
};
std::string mitk::ThreeStepLinearModel::GetModelType() const
{
return MODEL_TYPE;
};
mitk::ThreeStepLinearModel::FunctionStringType mitk::ThreeStepLinearModel::GetFunctionString() const
{
return FUNCTION_STRING;
};
std::string mitk::ThreeStepLinearModel::GetXName() const
{
return X_NAME;
};
std::string mitk::ThreeStepLinearModel::GetXAxisName() const
{
return X_AXIS_NAME;
};
std::string mitk::ThreeStepLinearModel::GetXAxisUnit() const
{
return X_AXIS_UNIT;
}
std::string mitk::ThreeStepLinearModel::GetYAxisName() const
{
return Y_AXIS_NAME;
};
std::string mitk::ThreeStepLinearModel::GetYAxisUnit() const
{
return Y_AXIS_UNIT;
}
mitk::ThreeStepLinearModel::ParameterNamesType
mitk::ThreeStepLinearModel::GetParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_PARAMETER_y_bl);
result.push_back(NAME_PARAMETER_x0);
result.push_back(NAME_PARAMETER_x1);
result.push_back(NAME_PARAMETER_b0);
result.push_back(NAME_PARAMETER_b1);
return result;
};
mitk::ThreeStepLinearModel::ParamterUnitMapType mitk::ThreeStepLinearModel::GetParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_PARAMETER_y_bl, UNIT_PARAMETER_y_bl));
result.insert(std::make_pair(NAME_PARAMETER_x0, UNIT_PARAMETER_x0));
result.insert(std::make_pair(NAME_PARAMETER_x1, UNIT_PARAMETER_x1));
result.insert(std::make_pair(NAME_PARAMETER_b0, UNIT_PARAMETER_b0));
result.insert(std::make_pair(NAME_PARAMETER_b1, UNIT_PARAMETER_b1));
return result;
}
mitk::ThreeStepLinearModel::ParametersSizeType
mitk::ThreeStepLinearModel::GetNumberOfParameters() const
{
return NUMBER_OF_PARAMETERS;
};
mitk::ThreeStepLinearModel::ParameterNamesType
mitk::ThreeStepLinearModel::GetDerivedParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_DERIVED_PARAMETER_auc);
result.push_back(NAME_DERIVED_PARAMETER_x_fin);
result.push_back(NAME_DERIVED_PARAMETER_y_fin);
result.push_back(NAME_DERIVED_PARAMETER_y_max);
result.push_back(NAME_DERIVED_PARAMETER_y1);
result.push_back(NAME_DERIVED_PARAMETER_y2);
return result;
};
mitk::ThreeStepLinearModel::ParametersSizeType
mitk::ThreeStepLinearModel::GetNumberOfDerivedParameters() const
{
return NUMBER_OF_DERIVED_PARAMETERS;
};
mitk::ThreeStepLinearModel::ParamterUnitMapType mitk::ThreeStepLinearModel::GetDerivedParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, UNIT_DERIVED_PARAMETER_auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_x_fin, UNIT_DERIVED_PARAMETER_x_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, UNIT_DERIVED_PARAMETER_y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, UNIT_DERIVED_PARAMETER_y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, UNIT_DERIVED_PARAMETER_y1));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y2, UNIT_DERIVED_PARAMETER_y2));
return result;
};
double mitk::ThreeStepLinearModel::ComputeSignalFromParameters(double x, double y_bl, double x0, double x1, double b0, double b1, double y1, double y2)
{
double signal = 0.0;
if (x < x0)
{
signal = y_bl;
}
else if (x >= x0 && x <= x1)
{
signal = b0 * x + y1;
}
else
{
signal = b1 * x + y2;
}
return signal;
};
mitk::ThreeStepLinearModel::ModelResultType
mitk::ThreeStepLinearModel::ComputeModelfunction(const ParametersType& parameters) const
{
//Model Parameters
const double y_bl = (double) parameters[POSITION_PARAMETER_y_bl];
const double x0 = (double) parameters[POSITION_PARAMETER_x0] ;
const double x1 = (double) parameters[POSITION_PARAMETER_x1] ;
const double b0 = (double) parameters[POSITION_PARAMETER_b0] ;
const double b1 = (double) parameters[POSITION_PARAMETER_b1] ;
double y1 = y_bl - b0 * x0;
double y2 = (b0 * x1 + y1) - (b1 * x1);
ModelResultType signal(m_TimeGrid.GetSize());
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
ModelResultType::iterator signalPos = signal.begin();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd; ++gridPos, ++signalPos)
{
*signalPos = ComputeSignalFromParameters(*gridPos, y_bl, x0, x1, b0, b1, y1, y2);
}
return signal;
};
mitk::ThreeStepLinearModel::ParameterNamesType mitk::ThreeStepLinearModel::GetStaticParameterNames() const
{
ParameterNamesType result;
return result;
}
mitk::ThreeStepLinearModel::ParametersSizeType mitk::ThreeStepLinearModel::GetNumberOfStaticParameters() const
{
return 0;
}
void mitk::ThreeStepLinearModel::SetStaticParameter(const ParameterNameType&,
const StaticParameterValuesType&)
{
//do nothing
};
mitk::ThreeStepLinearModel::StaticParameterValuesType mitk::ThreeStepLinearModel::GetStaticParameterValue(
const ParameterNameType&) const
{
StaticParameterValuesType result;
//do nothing
return result;
};
mitk::ModelBase::DerivedParameterMapType mitk::ThreeStepLinearModel::ComputeDerivedParameters(
const mitk::ModelBase::ParametersType& parameters) const
{
const double y_bl = (double) parameters[POSITION_PARAMETER_y_bl];
const double x0 = (double) parameters[POSITION_PARAMETER_x0] ;
const double x1 = (double) parameters[POSITION_PARAMETER_x1] ;
const double b0 = (double) parameters[POSITION_PARAMETER_b0] ;
const double b1 = (double) parameters[POSITION_PARAMETER_b1] ;
const double y1 = y_bl - b0 * x0;
const double y2 = (b0 * x1 + y1) - (b1 * x1);
unsigned int timeSteps = m_TimeGrid.GetSize();
- const double x_fin = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : ( mitkThrow() << "An exception occured because time grid is empty, method can't continue.");
+ const double x_fin = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : ( mitkThrow() << "An exception occurred because time grid is empty, method can't continue.");
const double y_fin = b1 * x_fin + y2;
double y_max = y_fin;
if ((b0 >= 0) && (b1 >= 0))
y_max = y_fin;
else if ((b0 < 0) && (b1 < 0))
y_max = y_bl;
else if ((b0 > 0) && (b1 < 0))
y_max = (b0 * x1 + y1);
else
{
if (abs(b0 * (x1 - x0)) >= abs(b1 * (x_fin - x1)))
y_max = y_bl;
else y_max = y_fin;
}
double auc = 0.0;
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd - 1; ++gridPos)
{
double currentGridPos = *gridPos;
double nextGridPos = *(++gridPos);
double deltaX = nextGridPos - currentGridPos;
double deltaY = ComputeSignalFromParameters(nextGridPos, y_bl, x0, x1, b0, b1, y1, y2) - ComputeSignalFromParameters(currentGridPos, y_bl, x0, x1, b0, b1, y1, y2);
double Yi = ComputeSignalFromParameters(currentGridPos, y_bl, x0, x1, b0, b1, y1, y2 );
double intI = 0.5 * deltaX * deltaY + Yi * deltaX;
auc += std::abs(intI);
--gridPos;
}
DerivedParameterMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_x_fin, x_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, y1));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y2, y2));
return result;
};
itk::LightObject::Pointer mitk::ThreeStepLinearModel::InternalClone() const
{
ThreeStepLinearModel::Pointer newClone = ThreeStepLinearModel::New();
newClone->SetTimeGrid(this->m_TimeGrid);
return newClone.GetPointer();
};
diff --git a/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp b/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp
index ab17e6770f..bc72b1d958 100644
--- a/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp
+++ b/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp
@@ -1,285 +1,285 @@
/*============================================================================
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 "mitkTwoStepLinearModel.h"
#include <mitkIOUtil.h>
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_y0 = "y-intercept";
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_x0 = "x_changepoint";
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_b0 = "slope1";
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_b1 = "slope2";
const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_PARAMETERS = 4;
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_y0 = "[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_x0 = "[unit of x]";
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_b0 = "[unit of y]/[unit of x]";
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_b1 = "[unit of y]/[unit of x]";
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_y0 = 0;
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_x0 = 1;
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_b0 = 2;
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_b1 = 3;
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_auc = "auc";
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_y_fin = "y_final";
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_y_max = "y_max";
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_y1 = "y-intercept1";
const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_DERIVED_PARAMETERS = 4;
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_auc = "[unit of x]*[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_y_fin = "[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_y_max = "[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_y1 = "[unit of y]";
const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_STATIC_PARAMETERS = 0;
const std::string mitk::TwoStepLinearModel::MODEL_DISPLAY_NAME = "Two Step Linear Model";
const std::string mitk::TwoStepLinearModel::MODEL_TYPE = "Generic";
const std::string mitk::TwoStepLinearModel::FUNCTION_STRING = "if x < x_changepoint: y(x) = y-intercept + slope1*x, else: y(x) = y-intercept1 + slope2*x";
const std::string mitk::TwoStepLinearModel::X_NAME = "x";
const std::string mitk::TwoStepLinearModel::X_AXIS_NAME = "X";
const std::string mitk::TwoStepLinearModel::X_AXIS_UNIT = "unit of x";
const std::string mitk::TwoStepLinearModel::Y_AXIS_NAME = "Y";
const std::string mitk::TwoStepLinearModel::Y_AXIS_UNIT = "unit of y";
std::string mitk::TwoStepLinearModel::GetModelDisplayName() const
{
return MODEL_DISPLAY_NAME;
};
std::string mitk::TwoStepLinearModel::GetModelType() const
{
return MODEL_TYPE;
};
mitk::TwoStepLinearModel::FunctionStringType mitk::TwoStepLinearModel::GetFunctionString() const
{
return FUNCTION_STRING;
};
std::string mitk::TwoStepLinearModel::GetXName() const
{
return X_NAME;
};
std::string mitk::TwoStepLinearModel::GetXAxisName() const
{
return X_AXIS_NAME;
};
std::string mitk::TwoStepLinearModel::GetXAxisUnit() const
{
return X_AXIS_UNIT;
}
std::string mitk::TwoStepLinearModel::GetYAxisName() const
{
return Y_AXIS_NAME;
};
std::string mitk::TwoStepLinearModel::GetYAxisUnit() const
{
return Y_AXIS_UNIT;
}
mitk::TwoStepLinearModel::ParameterNamesType
mitk::TwoStepLinearModel::GetParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_PARAMETER_y0);
result.push_back(NAME_PARAMETER_x0);
result.push_back(NAME_PARAMETER_b0);
result.push_back(NAME_PARAMETER_b1);
return result;
};
mitk::TwoStepLinearModel::ParamterUnitMapType mitk::TwoStepLinearModel::GetParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_PARAMETER_y0, UNIT_PARAMETER_y0));
result.insert(std::make_pair(NAME_PARAMETER_x0, UNIT_PARAMETER_x0));
result.insert(std::make_pair(NAME_PARAMETER_b0, UNIT_PARAMETER_b0));
result.insert(std::make_pair(NAME_PARAMETER_b1, UNIT_PARAMETER_b1));
return result;
}
mitk::TwoStepLinearModel::ParametersSizeType
mitk::TwoStepLinearModel::GetNumberOfParameters() const
{
return NUMBER_OF_PARAMETERS;
};
mitk::TwoStepLinearModel::ParameterNamesType
mitk::TwoStepLinearModel::GetDerivedParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_DERIVED_PARAMETER_auc);
result.push_back(NAME_DERIVED_PARAMETER_y_fin);
result.push_back(NAME_DERIVED_PARAMETER_y_max);
result.push_back(NAME_DERIVED_PARAMETER_y1);
return result;
};
mitk::TwoStepLinearModel::ParametersSizeType
mitk::TwoStepLinearModel::GetNumberOfDerivedParameters() const
{
return NUMBER_OF_DERIVED_PARAMETERS;
};
mitk::TwoStepLinearModel::ParamterUnitMapType mitk::TwoStepLinearModel::GetDerivedParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, UNIT_DERIVED_PARAMETER_auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, UNIT_DERIVED_PARAMETER_y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, UNIT_DERIVED_PARAMETER_y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, UNIT_DERIVED_PARAMETER_y1));
return result;
};
double mitk::TwoStepLinearModel::ComputeSignalFromParameters(double x, double x0, double b0, double b1, double y0, double y1)
{
return (x < x0) ? (b0 * x + y0) : (b1 * x + y1);
};
mitk::TwoStepLinearModel::ModelResultType
mitk::TwoStepLinearModel::ComputeModelfunction(const ParametersType& parameters) const
{
//Model Parameters
const auto y0 = parameters[POSITION_PARAMETER_y0];
const auto x0 = parameters[POSITION_PARAMETER_x0] ;
const auto b0 = parameters[POSITION_PARAMETER_b0] ;
const auto b1 = parameters[POSITION_PARAMETER_b1] ;
double y1 = (b0 - b1) * x0 + y0;
ModelResultType signal(m_TimeGrid.GetSize());
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
ModelResultType::iterator signalPos = signal.begin();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd; ++gridPos, ++signalPos)
{
*signalPos = ComputeSignalFromParameters(*gridPos, x0, b0, b1, y0, y1);
}
return signal;
};
mitk::TwoStepLinearModel::ParameterNamesType mitk::TwoStepLinearModel::GetStaticParameterNames() const
{
ParameterNamesType result;
return result;
}
mitk::TwoStepLinearModel::ParametersSizeType mitk::TwoStepLinearModel::GetNumberOfStaticParameters() const
{
return 0;
}
void mitk::TwoStepLinearModel::SetStaticParameter(const ParameterNameType& /*name*/,
const StaticParameterValuesType& /*values*/)
{
//do nothing
};
mitk::TwoStepLinearModel::StaticParameterValuesType mitk::TwoStepLinearModel::GetStaticParameterValue(
const ParameterNameType& /*name*/) const
{
StaticParameterValuesType result;
//do nothing
return result;
};
mitk::ModelBase::DerivedParameterMapType mitk::TwoStepLinearModel::ComputeDerivedParameters(
const mitk::ModelBase::ParametersType& parameters) const
{
const auto y0 = parameters[POSITION_PARAMETER_y0];
const auto x0 = parameters[POSITION_PARAMETER_x0] ;
const auto b0 = parameters[POSITION_PARAMETER_b0] ;
const auto b1 = parameters[POSITION_PARAMETER_b1] ;
const auto y1 = (b0 - b1) * x0 + y0;
unsigned int timeSteps = m_TimeGrid.GetSize();
- const double taq = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : (mitkThrow() << "An exception occured because time grid is empty, method can't continue.");
+ const double taq = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : (mitkThrow() << "An exception occurred because time grid is empty, method can't continue.");
const double y_fin = b1 * taq + y1;
double y_max = y_fin;
if ((b0 >= 0) && (b1 >= 0))
y_max = y_fin;
else if ((b0 < 0) && (b1 < 0))
y_max = y0;
else if ((b0 > 0) && (b1 < 0))
y_max = (b0 * x0 + y0);
else
{
if (abs(b0 * x0) >= abs(b1 * (taq - x0)))
y_max = y0;
else y_max = y_fin;
}
double auc = 0.0;
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd -1; ++gridPos)
{
double currentGridPos = *gridPos;
double nextGridPos = *(++gridPos);
double deltaX = nextGridPos - currentGridPos;
double deltaY = ComputeSignalFromParameters(nextGridPos, x0, b0, b1, y0, y1) - ComputeSignalFromParameters(currentGridPos, x0, b0, b1, y0, y1);
double Yi = ComputeSignalFromParameters(currentGridPos, x0, b0, b1, y0, y1);
double intI = 0.5 * deltaX * deltaY + Yi * deltaX;
auc += std::abs(intI);
--gridPos;
}
DerivedParameterMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, y1));
return result;
};
itk::LightObject::Pointer mitk::TwoStepLinearModel::InternalClone() const
{
TwoStepLinearModel::Pointer newClone = TwoStepLinearModel::New();
newClone->SetTimeGrid(this->m_TimeGrid);
return newClone.GetPointer();
};
diff --git a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp
index 692892d094..c082dd9fc6 100644
--- a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp
+++ b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp
@@ -1,425 +1,425 @@
/*============================================================================
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 <iostream>
#include "mitkProperties.h"
#include "mitkStandaloneDataStorage.h"
#include "mitkModelFitInfo.h"
#include "mitkModelFitConstants.h"
#include "mitkModelFitException.h"
#include "mitkModelFitResultRelationRule.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkUIDGenerator.h>
mitk::modelFit::ModelFitInfo::UIDType ensureModelFitUID(mitk::BaseData * data)
{
mitk::BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str());
std::string propUID = "";
if (uidProp.IsNotNull())
{
propUID = uidProp->GetValueAsString();
}
else
{
mitk::UIDGenerator generator;
propUID = generator.GetUID();
data->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(propUID));
}
return propUID;
};
mitk::DataNode::Pointer generateModelFitTestNode()
{
mitk::DataNode::Pointer node = mitk::DataNode::New();
node->SetName("Param1");
auto testImage = mitk::Image::New();
node->SetData(testImage);
testImage->SetProperty("modelfit.testEmpty", mitk::StringProperty::New(""));
testImage->SetProperty("modelfit.testValid", mitk::StringProperty::New("test"));
ensureModelFitUID(testImage);
testImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy"));
testImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName"));
testImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
testImage->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("input UID"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
testImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
testImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
testImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
testImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
return node;
}
class mitkModelFitInfoTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkModelFitInfoTestSuite);
MITK_TEST(CheckModelFitInfo);
MITK_TEST(CheckGetMandatoryProperty);
MITK_TEST(CheckCreateFitInfoFromNode_Legacy);
MITK_TEST(CheckCreateFitInfoFromNode);
MITK_TEST(CheckGetNodesOfFit);
MITK_TEST(CheckGetFitUIDsOfNode);
CPPUNIT_TEST_SUITE_END();
mitk::StandaloneDataStorage::Pointer m_Storage;
mitk::Image::Pointer m_ParamImage;
mitk::Image::Pointer m_ParamImage2;
mitk::Image::Pointer m_ParamImage_legacy;
mitk::Image::Pointer m_ParamImage2_legacy;
mitk::DataNode::Pointer m_ParamImageNode;
mitk::DataNode::Pointer m_ParamImageNode2;
mitk::DataNode::Pointer m_ParamImageNode_legacy;
mitk::DataNode::Pointer m_ParamImageNode2_legacy;
public:
void setUp() override
{
m_Storage = mitk::StandaloneDataStorage::New();
//create input node
mitk::DataNode::Pointer inputNode = mitk::DataNode::New();
inputNode->SetName("Input");
mitk::Image::Pointer image = mitk::Image::New();
inputNode->SetData(image);
mitk::modelFit::ModelFitInfo::UIDType inputUID = ensureModelFitUID(image);
m_Storage->Add(inputNode);
mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New();
parents->push_back(inputNode);
/////////////////////////////////////////////////////
//Create nodes for a fit (new style using rules)
/////////////////////////////////////////////////////
auto rule = mitk::ModelFitResultRelationRule::New();
//create first result for FitLegacy
m_ParamImageNode = mitk::DataNode::New();
m_ParamImage = mitk::Image::New();
m_ParamImageNode->SetData(m_ParamImage);
m_ParamImageNode->SetName("Param1");
m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str()));
rule->Connect(m_ParamImage, image);
m_Storage->Add(m_ParamImageNode, parents);
//create second result for Fit
m_ParamImageNode2 = mitk::DataNode::New();
m_ParamImageNode2->SetName("Param2");
m_ParamImage2 = mitk::Image::New();
m_ParamImageNode2->SetData(m_ParamImage2);
m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str()));
rule->Connect(m_ParamImage2, image);
m_Storage->Add(m_ParamImageNode2, parents);
/////////////////////////////////////////////////////
//Create nodes for a fit in legacy mode (old fit uid)
/////////////////////////////////////////////////////
//create first result for FitLegacy
m_ParamImageNode_legacy = mitk::DataNode::New();
m_ParamImage_legacy = mitk::Image::New();
m_ParamImageNode_legacy->SetData(m_ParamImage_legacy);
m_ParamImageNode_legacy->SetName("Param1_legacy");
ensureModelFitUID(m_ParamImage_legacy);
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1_legacy"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str()));
m_Storage->Add(m_ParamImageNode_legacy, parents);
//create second result for FitLegacy
m_ParamImageNode2_legacy = mitk::DataNode::New();
m_ParamImageNode2_legacy->SetName("Param2_legacy");
m_ParamImage2_legacy = mitk::Image::New();
m_ParamImageNode2_legacy->SetData(m_ParamImage2_legacy);
ensureModelFitUID(m_ParamImage2_legacy);
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2_legacy"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str()));
m_Storage->Add(m_ParamImageNode2_legacy, parents);
/////////////////////////////////////////////////////
//Create nodes for a fit on other input
auto anotherInputNode = mitk::DataNode::New();
anotherInputNode->SetName("AnotherInput");
auto anotherImage = mitk::Image::New();
anotherInputNode->SetData(anotherImage);
m_Storage->Add(anotherInputNode);
parents = mitk::DataStorage::SetOfObjects::New();
parents->push_back(anotherInputNode);
mitk::DataNode::Pointer node3 = mitk::DataNode::New();
node3->SetName("Param_Other");
mitk::Image::Pointer paramImage3 = mitk::Image::New();
node3->SetData(paramImage3);
paramImage3->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit2"));
paramImage3->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName2"));
paramImage3->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_2"));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass_B"));
paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param_Other"));
paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a"));
rule->Connect(paramImage3, anotherImage);
m_Storage->Add(node3, parents);
}
void tearDown() override
{
}
void CheckModelFitInfo()
{
mitk::modelFit::Parameter::Pointer p = mitk::modelFit::Parameter::New();
p->name = "p";
mitk::modelFit::ModelFitInfo::Pointer fit = mitk::modelFit::ModelFitInfo::New();
fit->AddParameter(p);
CPPUNIT_ASSERT_MESSAGE("AddParameter unsuccessfully adds a parameter.", fit->GetParameters().size() == 1);
mitk::modelFit::Parameter::ConstPointer resultParam = fit->GetParameter("test",
mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns NULL for wrong parameter.", resultParam.IsNull());
resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter.", resultParam == p);
p->type = mitk::modelFit::Parameter::CriterionType;
resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::CriterionType);
CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter with a non-default type.", resultParam == p);
fit->DeleteParameter("test", mitk::modelFit::Parameter::CriterionType);
CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter fails for wrong parameter.", fit->GetParameters().size() == 1);
fit->DeleteParameter("p", mitk::modelFit::Parameter::CriterionType);
CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter successfully removes a parameter.", fit->GetParameters().size() == 0);
}
void CheckGetMandatoryProperty()
{
mitk::DataNode::Pointer node = generateModelFitTestNode();
mitk::DataNode::Pointer invalideNode = mitk::DataNode::New();
CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testInvalid"), mitk::modelFit::ModelFitException);
CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testEmpty"), mitk::modelFit::ModelFitException);
CPPUNIT_ASSERT_MESSAGE("Testing if GetMandatoryProperty returns the correct value.", mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testValid")
== "test");
}
void CheckCreateFitInfoFromNode_Legacy()
{
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("FitLegacy", nullptr).IsNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull());
mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("FitLegacy", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.",
resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() &&
resultFit->uid == "FitLegacy" &&
resultFit->fitName == "MyName1" &&
resultFit->modelType == "TestModels" &&
resultFit->modelName == "TestModel_1" &&
resultFit->function == "" &&
resultFit->functionClassID == "ModelClass" &&
resultFit->x == "myX" &&
resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() &&
resultFit->xAxisUnit == "h" &&
resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() &&
resultFit->yAxisUnit == "kg" &&
resultFit->GetParameters().size() == 2);
mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1_legacy", mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1_legacy" && param->unit == "b" && param->image == m_ParamImage_legacy);
mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2_legacy", mitk::modelFit::Parameter::DerivedType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2_legacy" && param2->unit == "a" && param2->image == m_ParamImage2_legacy);
}
void CheckCreateFitInfoFromNode()
{
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("Fit", nullptr).IsNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull());
mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("Fit", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.",
resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() &&
resultFit->uid == "Fit" &&
resultFit->fitName == "MyName1" &&
resultFit->modelType == "TestModels" &&
resultFit->modelName == "TestModel_1" &&
resultFit->function == "" &&
resultFit->functionClassID == "ModelClass" &&
resultFit->x == "myX" &&
resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() &&
resultFit->xAxisUnit == "h" &&
resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() &&
resultFit->yAxisUnit == "kg" &&
resultFit->GetParameters().size() == 2);
mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1", mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1" && param->unit == "b" && param->image == m_ParamImage);
mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2", mitk::modelFit::Parameter::DerivedType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2" && param2->unit == "a" && param2->image == m_ParamImage2);
}
void CheckGetNodesOfFit()
{
auto nodes = mitk::modelFit::GetNodesOfFit("Fit", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit",
nodes->Size() == 2);
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode.GetPointer()) != nodes->end());
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2.GetPointer()) != nodes->end());
nodes = mitk::modelFit::GetNodesOfFit("FitLegacy", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for FitLegacy",
nodes->Size() == 2);
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode_legacy.GetPointer()) != nodes->end());
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2_legacy.GetPointer()) != nodes->end());
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit2",
mitk::modelFit::GetNodesOfFit("Fit2", m_Storage)->Size() == 1);
- CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unkown fits.",
+ CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unknown fits.",
mitk::modelFit::GetNodesOfFit("unkown_fit", m_Storage)->Size() == 0);
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for illegal calls.",
mitk::modelFit::GetNodesOfFit("unkown_fit", nullptr).IsNull());
}
void CheckGetFitUIDsOfNode()
{
auto testNode = m_Storage->GetNamedNode("Input");
mitk::modelFit::NodeUIDSetType uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.",
uidSet.size() == 2 &&
uidSet.find("Fit") != uidSet.end() &&
uidSet.find("FitLegacy") != uidSet.end());
testNode = m_Storage->GetNamedNode("AnotherInput");
uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.",
uidSet.size() == 1 &&
uidSet.find("Fit2") != uidSet.end());
uidSet = mitk::modelFit::GetFitUIDsOfNode(nullptr, m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid node.",
uidSet.size() == 0);
uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, nullptr);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid storage.",
uidSet.size() == 0);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkModelFitInfo)
\ No newline at end of file
diff --git a/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp b/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp
index 7fb29e0d42..5e70556220 100644
--- a/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp
+++ b/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp
@@ -1,732 +1,732 @@
/*============================================================================
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 "mitkModelFitResultRelationRule.h"
#include "mitkDataNode.h"
#include "mitkPointSet.h"
#include "mitkStringProperty.h"
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
#include "mitkPropertyNameHelper.h"
#include "mitkTemporoSpatialStringProperty.h"
#include "mitkPropertyNameHelper.h"
#include "mitkModelFitConstants.h"
#include "mitkUIDGenerator.h"
#include <regex>
class mitkModelFitResultRelationRuleTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkModelFitResultRelationRuleTestSuite);
MITK_TEST(ConstructionAndGetter);
MITK_TEST(IsSourceCandidate);
MITK_TEST(IsDestinationCandidate);
MITK_TEST(IsSource);
MITK_TEST(HasRelation);
MITK_TEST(GetExistingRelations);
MITK_TEST(GetRelationUIDs);
MITK_TEST(GetSourceCandidateIndicator);
MITK_TEST(GetDestinationCandidateIndicator);
MITK_TEST(GetConnectedSourcesDetector);
MITK_TEST(GetSourcesDetector);
MITK_TEST(Connect);
MITK_TEST(Disconnect);
CPPUNIT_TEST_SUITE_END();
private:
mitk::ModelFitResultRelationRule::Pointer rule;
mitk::Image::Pointer unRelated;
mitk::DataNode::Pointer unRelated_Node;
mitk::Image::Pointer source_implicit_1;
mitk::DataNode::Pointer source_implicit_1_Node;
mitk::Image::Pointer source_Data_1;
mitk::DataNode::Pointer source_Data_1_Node;
mitk::Image::Pointer source_idOnly_1;
mitk::DataNode::Pointer source_idOnly_1_Node;
mitk::Image::Pointer source_1;
mitk::DataNode::Pointer source_1_Node;
mitk::Image::Pointer source_legacy_1;
mitk::DataNode::Pointer source_legacy_1_Node;
mitk::Image::Pointer source_otherRule;
mitk::DataNode::Pointer source_otherRule_Node;
mitk::Image::Pointer source_otherPurpose;
mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not.
mitk::DataNode::Pointer dest_1_Node;
mitk::Image::Pointer dest_1;
mitk::DataNode::Pointer dest_2_Node;
mitk::Image::Pointer dest_2;
bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const
{
auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath();
if (!instance.empty())
{
keyPath.AddElement(instance);
}
auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath);
auto keys = provider->GetPropertyKeys();
for (const auto &key : keys)
{
if (key.find(prefix) == 0)
{
return true;
}
}
return false;
}
std::vector<std::string> GetReferenceSequenceIndices(const mitk::IPropertyProvider * source,
const mitk::IPropertyProvider * destination) const
{
std::vector<std::string> result;
auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018));
if (destInstanceUIDProp.IsNull())
{
return result;
}
mitk::PropertyKeyPath referencedInstanceUIDs;
referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155");
auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);;
auto regEx = std::regex(sourceRegExStr);
std::vector<std::string> keys;
//workaround until T24729 is done. Please remove if T24728 is done
keys = source->GetPropertyKeys();
//end workaround for T24729
for (const auto &key : keys)
{
if (std::regex_match(key, regEx))
{
auto refUIDProp = source->GetConstProperty(key);
if (*refUIDProp == *destInstanceUIDProp)
{
mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key);
result.push_back(std::to_string(finding.GetNode(2).selection));
}
}
}
return result;
};
void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement)
{
mitk::PropertyKeyPath refInstanceUIDPath;
refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155");
owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID));
mitk::PropertyKeyPath refClassUIDPath;
refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150");
owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID));
mitk::PropertyKeyPath purposePath;
purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose));
}
bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const
{
mitk::PropertyKeyPath refInstanceUIDPath;
refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155");
auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath));
if (prop->GetValueAsString() != instanceUID)
{
return false;
}
mitk::PropertyKeyPath refClassUIDPath;
refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150");
prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath));
if (prop->GetValueAsString() != classUID)
{
return false;
}
mitk::PropertyKeyPath purposePath;
purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath));
if (prop->GetValueAsString() != purpose)
{
return false;
}
return true;
}
public:
void setUp() override
{
auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018);
auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016);
rule = mitk::ModelFitResultRelationRule::New();
unRelated = mitk::Image::New();
unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated"));
unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image"));
unRelated_Node = mitk::DataNode::New();
unRelated_Node->SetData(unRelated);
dest_1_Node = mitk::DataNode::New();
dest_1_Node->SetName("dest_1");
dest_1 = mitk::Image::New();
dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1"));
dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image"));
dest_1->GetTimeGeometry()->Expand(2);
dest_1_Node->SetData(dest_1);
//support of legacy mode
mitk::UIDGenerator generator;
auto legacyUID = generator.GetUID();
dest_1->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(legacyUID));
dest_2_Node = mitk::DataNode::New();
dest_2_Node->SetName("dest_2");
dest_2 = mitk::Image::New();
dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2"));
dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image"));
dest_2->GetTimeGeometry()->Expand(3);
dest_2_Node->SetData(dest_2);
source_implicit_1 = mitk::Image::New();
SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Model fit input", 0);
source_implicit_1_Node = mitk::DataNode::New();
source_implicit_1_Node->SetData(source_implicit_1);
source_idOnly_1 = mitk::Image::New();
std::string name = "MITK.Relations.1.relationUID";
source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1"));
name = "MITK.Relations.1.destinationUID";
source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID()));
source_idOnly_1_Node = mitk::DataNode::New();
source_idOnly_1_Node->SetData(source_idOnly_1);
source_Data_1 = mitk::Image::New();
SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Model fit input", 0);
SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1);
name = "MITK.Relations.1.relationUID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2"));
name = "MITK.Relations.1.ruleID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID()));
name = "MITK.Relations.1.SourceImageSequenceItem";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0"));
name = "MITK.Relations.2.relationUID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10"));
name = "MITK.Relations.2.SourceImageSequenceItem";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1"));
name = "MITK.Relations.2.ruleID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose"));
source_Data_1_Node = mitk::DataNode::New();
source_Data_1_Node->SetData(source_Data_1);
source_1 = mitk::Image::New();
SetDICOMReferenceInfo(source_1, "dest_1", "image", "Model fit input", 0);
SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1);
name = "MITK.Relations.1.relationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3"));
name = "MITK.Relations.1.destinationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID()));
name = "MITK.Relations.1.SourceImageSequenceItem";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0"));
name = "MITK.Relations.2.relationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8"));
name = "MITK.Relations.2.destinationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID()));
name = "MITK.Relations.2.ruleID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose"));
name = "MITK.Relations.2.SourceImageSequenceItem";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1"));
source_1_Node = mitk::DataNode::New();
source_1_Node->SetData(source_1);
source_legacy_1 = mitk::Image::New();
source_legacy_1->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(legacyUID.c_str()));
source_legacy_1_Node = mitk::DataNode::New();
source_legacy_1_Node->SetData(source_legacy_1);
source_otherRule = mitk::Image::New();
name = "MITK.Relations.1.relationUID";
source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7"));
name = "MITK.Relations.1.destinationUID";
source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID"));
source_otherRule_Node = mitk::DataNode::New();
source_otherRule_Node->SetData(source_otherRule);
source_otherPurpose = mitk::Image::New();
name = "MITK.Relations.1.relationUID";
source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9"));
name = "MITK.Relations.1.destinationUID";
source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose"));
source_otherPurpose_Node = mitk::DataNode::New();
source_otherPurpose_Node->SetData(source_otherPurpose);
}
void tearDown() override {}
void ConstructionAndGetter()
{
CPPUNIT_ASSERT(!rule->IsAbstract());
CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Model fit input"));
CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("Model fit result relation"));
CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("fit result"));
CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image"));
}
void IsSourceCandidate()
{
CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New()));
CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr));
}
void IsDestinationCandidate()
{
CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node));
CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1));
CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New()));
CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr));
CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::Image::New()));
}
void IsSource()
{
CPPUNIT_ASSERT_THROW_MESSAGE(
"Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject);
CPPUNIT_ASSERT(!rule->IsSource(unRelated));
CPPUNIT_ASSERT(rule->IsSource(source_implicit_1));
CPPUNIT_ASSERT(rule->IsSource(source_Data_1));
CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1));
CPPUNIT_ASSERT(rule->IsSource(source_1));
CPPUNIT_ASSERT(rule->IsSource(source_legacy_1));
CPPUNIT_ASSERT(!rule->IsSource(source_otherRule));
CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose));
CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node));
CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node));
CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node));
CPPUNIT_ASSERT(rule->IsSource(source_1_Node));
CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node));
CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node));
}
void HasRelation()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->HasRelation(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->HasRelation(source_1, nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(!rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None));
}
void GetExistingRelations()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->GetExistingRelations(nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty());
CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty());
CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty());
auto uids = rule->GetExistingRelations(source_implicit_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155");
uids = rule->GetExistingRelations(source_idOnly_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid1");
uids = rule->GetExistingRelations(source_Data_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid2");
uids = rule->GetExistingRelations(source_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid3");
uids = rule->GetExistingRelations(source_legacy_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "model.fit.input.image.legacy.relation");
}
void GetRelationUIDs()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->GetRelationUIDs(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->GetRelationUIDs(source_1, nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1");
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2");
auto uids = rule->GetRelationUIDs(source_1, dest_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid3");
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_legacy_1, dest_1).front() == "model.fit.input.image.legacy.relation");
}
void GetSourceCandidateIndicator()
{
auto predicate = rule->GetSourceCandidateIndicator();
CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New()));
CPPUNIT_ASSERT(!predicate->CheckNode(nullptr));
}
void GetDestinationCandidateIndicator()
{
auto predicate = rule->GetDestinationCandidateIndicator();
CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New()));
CPPUNIT_ASSERT(!predicate->CheckNode(nullptr));
CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New()));
}
void GetConnectedSourcesDetector()
{
auto predicate = rule->GetConnectedSourcesDetector();
CPPUNIT_ASSERT(!predicate->CheckNode(nullptr));
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
}
void GetSourcesDetector()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->GetSourcesDetector(nullptr),
itk::ExceptionObject);
auto predicate = rule->GetSourcesDetector(dest_1);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node));
}
void Connect()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->Connect(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->Connect(source_1, nullptr),
itk::ExceptionObject);
// check upgrade of an implicit connection
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
rule->Connect(source_implicit_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Model fit input", 0));
// check upgrade and reuse of an Data connection (no new relation should be generated).
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
rule->Connect(source_Data_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1);
CPPUNIT_ASSERT(relUID.size() == 1);
std::string name = "MITK.Relations.1.destinationUID";
auto prop = source_Data_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE(
"Destination uid was not stored with the correct key. Already existing session should be used.", prop);
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = source_Data_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = source_Data_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Model fit input", 0));
// check actualization of an id only connection
rule->Connect(source_idOnly_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
- CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.",
+ CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.",
rule->GetExistingRelations(source_1).size() == 1);
// check actualization of an existing connection
rule->Connect(source_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
- CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.",
+ CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.",
rule->GetExistingRelations(source_1).size() == 1);
name = "MITK.Relations.1.destinationUID";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE(
"Destination uid was not stored with the correct key. Already existing session should be used.", prop);
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
dcmRefs = GetReferenceSequenceIndices(source_1, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0));
// check creation of an new connection
rule->Connect(unRelated, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
- CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.",
+ CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating existing one.",
rule->GetExistingRelations(unRelated).size() == 1);
name = "MITK.Relations.1.destinationUID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
- CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0");
+ CPPUNIT_ASSERT_MESSAGE("Dicom reference sequence is corrupted. Should be just an index 0.", dcmRefs[0] == "0");
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Model fit input", 0));
// check creation of a 2nd connection of the same purpose
rule->Connect(unRelated, dest_2);
CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.",
rule->GetExistingRelations(unRelated).size() == 2);
name = "MITK.Relations.1.destinationUID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
name = "MITK.Relations.2.destinationUID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID());
name = "MITK.Relations.2.ruleID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.2.SourceImageSequenceItem";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1");
dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Model fit input", 0));
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Model fit input", 1));
}
void Disconnect()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->Disconnect(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->Disconnect(source_1, nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->Disconnect(nullptr, "uid"),
itk::ExceptionObject);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
rule->Disconnect(source_1, unRelated);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0));
//check if index correction is correct, when disconnecting
rule->Connect(source_1, dest_2);
rule->Connect(source_1, unRelated);
rule->Disconnect(source_1, dest_2);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1"));
CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2"));
CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3"));
CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4"));
CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0));
CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1));
CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Model fit input", 2));
std::string name = "MITK.Relations.4.destinationUID";
auto prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE(
"Destination uid was not stored with the correct key. Already existing session should be used.", prop);
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID());
name = "MITK.Relations.4.SourceImageSequenceItem";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2");
rule->Disconnect(source_otherPurpose, dest_1);
CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1"));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkModelFitResultRelationRule)
diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp
index a225d2ae4f..5d8e1c4e92 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp
+++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp
@@ -1,350 +1,350 @@
/*============================================================================
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 "mitkExceptionMacro.h"
#include "mitkModelFitParameterValueExtraction.h"
#include "QmitkFitParameterModel.h"
QmitkFitParameterModel::
QmitkFitParameterModel(QObject* parent) :
QAbstractTableModel(parent)
{
m_CurrentPos.Fill(0.0);
}
const QmitkFitParameterModel::FitVectorType&
QmitkFitParameterModel::
getFits() const
{
return m_Fits;
};
mitk::Point3D
QmitkFitParameterModel::
getCurrentPosition() const
{
return m_CurrentPos;
};
const mitk::PointSet*
QmitkFitParameterModel::
getPositionBookmarks() const
{
return m_Bookmarks;
};
void
QmitkFitParameterModel::
setFits(const FitVectorType& fits)
{
emit beginResetModel();
m_Fits = fits;
emit endResetModel();
};
void
QmitkFitParameterModel::
setCurrentPosition(const mitk::Point3D& currentPos)
{
emit beginResetModel();
m_CurrentPos = currentPos;
emit endResetModel();
};
void
QmitkFitParameterModel::
setPositionBookmarks(const mitk::PointSet* bookmarks)
{
emit beginResetModel();
m_Bookmarks = bookmarks;
emit endResetModel();
};
bool
QmitkFitParameterModel::
hasSingleFit() const
{
return this->m_Fits.size() == 1;
};
int
QmitkFitParameterModel::
rowCount(const QModelIndex& parent) const
{
if (this->hasSingleFit())
{
if (parent.isValid())
{
return 0;
}
else
{
return this->m_Fits.front()->GetParameters().size() + this->m_Fits.front()->staticParamMap.Size();
}
}
else
{
if (parent.isValid())
{
auto row = static_cast<std::size_t>(parent.row());
assert(row < this->m_Fits.size());
return this->m_Fits[row]->GetParameters().size() + this->m_Fits[row]->staticParamMap.Size();
}
else
{
return this->m_Fits.size();
}
}
}
std::size_t
QmitkFitParameterModel::
getBookmarksCount() const
{
if (m_Bookmarks.IsNotNull())
{
return m_Bookmarks->GetSize();
}
return 0;
}
int
QmitkFitParameterModel::
columnCount(const QModelIndex&) const
{
return 3 + this->getBookmarksCount();
}
/** Helper function returns the name of the static parameter indicates by the index.
If the index does not indicate a static parameter an empty string will be returned.*/
std::string GetStaticParameterName(const mitk::modelFit::ModelFitInfo* currentFit, const QModelIndex& index)
{
const auto paramSize = static_cast<int>(currentFit->GetParameters().size());
std::string staticParamName;
if (index.row() >= paramSize)
{
int pos = paramSize;
for (const auto& iter : currentFit->staticParamMap)
{
if (pos == index.row())
{
staticParamName = iter.first;
break;
}
++pos;
}
}
return staticParamName;
}
QVariant
QmitkFitParameterModel::
data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
QVariant result;
if (!index.parent().isValid() && !this->hasSingleFit())
{ //we need the fit names
if (index.row() < static_cast<int>(m_Fits.size()) && index.column() == 0)
{
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
result = QVariant(QString::fromStdString(m_Fits[index.row()]->fitName)+QString("(") + QString::fromStdString(m_Fits[index.row()]->uid) + QString(")"));
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Name (UID) of the fit.");
}
}
}
else
- { // realy want to get the values of the current fit
+ { // really want to get the values of the current fit
const mitk::modelFit::ModelFitInfo* currentFit = nullptr;
if (this->hasSingleFit() && !index.parent().isValid())
{
currentFit = m_Fits.front();
}
else if (index.parent().isValid() && index.parent().row() < static_cast<int>(m_Fits.size()))
{
currentFit = m_Fits[index.parent().row()];
}
if (currentFit)
{
const auto paramSize = static_cast<int>(currentFit->GetParameters().size());
const auto staticParamSize = static_cast<int>(currentFit->staticParamMap.Size());
if (index.row() < paramSize + staticParamSize)
{
std::string staticParamName = GetStaticParameterName(currentFit, index);
switch (index.column())
{
case 0:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() < paramSize)
{
const auto& param = currentFit->GetParameters()[index.row()];
result = QVariant(QString::fromStdString(param->name));
}
else
{
result = QVariant(QString::fromStdString(staticParamName));
}
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Name of the parameter.");
}
break;
case 1:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() < paramSize)
{
const auto& param = currentFit->GetParameters()[index.row()];
std::string paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER();
if (param->type == mitk::modelFit::Parameter::DerivedType)
{
paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER();
}
else if (param->type == mitk::modelFit::Parameter::CriterionType)
{
paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_CRITERION();
}
else if (param->type == mitk::modelFit::Parameter::EvaluationType)
{
paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER();
}
result = QVariant(QString::fromStdString(paramType));
}
else
{
result = QVariant("static");
}
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Type of the parameter.");
}
break;
default:
if (index.column() - 2 < static_cast<int>(this->getBookmarksCount()+1))
{
mitk::Point3D pos = m_CurrentPos;
if (index.column() > 2)
{
pos = m_Bookmarks->GetPoint(index.column() - 3);
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() < paramSize)
{
auto value = mitk::ReadVoxel(currentFit->GetParameters()[index.row()]->image, pos);
result = QVariant(QString::number(value));
}
else
{
QString concatenatedValues;
for (const auto &value : currentFit->staticParamMap.Get(staticParamName))
{
concatenatedValues += QString::number(value) + ", ";
}
if (!concatenatedValues.isEmpty())
{
concatenatedValues.chop(2);
}
result = QVariant(concatenatedValues);
}
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Value of a (static) fit parameter");
}
}
break;
}
}
}
}
return result;
}
Qt::ItemFlags
QmitkFitParameterModel::
flags(const QModelIndex& index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
return flags;
}
QVariant
QmitkFitParameterModel::
headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) &&
(Qt::Horizontal == orientation))
{
if (section == 0)
{
return QVariant("Name");
}
else if (section == 1)
{
return QVariant("Type");
}
else if (section == 2)
{
return QVariant("Value");
}
else if (section - 3 < static_cast<int>(this->getBookmarksCount()))
{
const auto & pos = m_Bookmarks->GetPoint(section - 3);
std::ostringstream strm;
strm.imbue(std::locale("C"));
strm << std::setprecision(3) << "Value @ Pos " << section -3 << " (" << pos[0] << "|" << pos[1] << "|" << pos[2] << ")";
return QVariant(QString::fromStdString(strm.str()));
}
}
return QVariant();
}
bool
QmitkFitParameterModel::
setData(const QModelIndex&, const QVariant&, int)
{
return false;
};
diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h
index e33b05eb6e..978fc33f46 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h
@@ -1,76 +1,76 @@
/*============================================================================
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 QmitkFitParameterModel_h
#define QmitkFitParameterModel_h
#include <QAbstractTableModel>
#include "mitkModelFitInfo.h"
#include "mitkPointSet.h"
#include "MitkModelFitUIExports.h"
/*!
\class QmitkFitParameterModel
Model that can be used to display the parameter values of ModelFitInfo instances for different world coordinate positions.
-If more then one ModelFitInfo instance is given the model will use a tree hirarchy. The first level are the fits,
+If more then one ModelFitInfo instance is given the model will use a tree hierarchy. The first level are the fits,
the seconds level are the parameter of the fit.
*/
class MITKMODELFITUI_EXPORT QmitkFitParameterModel : public QAbstractTableModel
{
Q_OBJECT
public:
using FitVectorType = std::vector<mitk::modelFit::ModelFitInfo::ConstPointer>;
QmitkFitParameterModel(QObject* parent = nullptr);
~QmitkFitParameterModel() override {};
const FitVectorType& getFits() const;
mitk::Point3D getCurrentPosition() const;
const mitk::PointSet* getPositionBookmarks() const;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex& index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
public Q_SLOTS:
void setFits(const FitVectorType& fits);
void setCurrentPosition(const mitk::Point3D& currentPos);
void setPositionBookmarks(const mitk::PointSet* bookmarks);
protected:
std::size_t getBookmarksCount() const;
private:
bool hasSingleFit() const;
FitVectorType m_Fits;
mitk::PointSet::ConstPointer m_Bookmarks;
mitk::Point3D m_CurrentPos;
};
#endif
diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h
index e5e9025f9c..fd40e0297d 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h
@@ -1,77 +1,77 @@
/*============================================================================
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 QmitkFitParameterWidget_h
#define QmitkFitParameterWidget_h
#include "mitkModelFitInfo.h"
#include "mitkPointSet.h"
#include "MitkModelFitUIExports.h"
#include "ui_QmitkFitParameterWidget.h"
#include <QWidget>
class QmitkFitParameterModel;
/**
* \class QmitkFitParameterWidget
* Widget that displays the parameters of all set ModelFitInfo instances for all given
* world coordinate points.
* In addition it allows to transfer this information as CSV into the clipboard or a file.
*/
class MITKMODELFITUI_EXPORT QmitkFitParameterWidget : public QWidget
{
Q_OBJECT
public:
using FitVectorType = std::vector<mitk::modelFit::ModelFitInfo::ConstPointer>;
QmitkFitParameterWidget(QWidget* parent = nullptr);
~QmitkFitParameterWidget() override;
const FitVectorType& getFits() const;
mitk::Point3D getCurrentPosition() const;
const mitk::PointSet* getPositionBookmarks() const;
public Q_SLOTS:
void setFits(const FitVectorType& fits);
void setCurrentPosition(const mitk::Point3D& currentPos);
void setPositionBookmarks(const mitk::PointSet* bookmarks);
protected Q_SLOTS:
void OnExportClicked() const;
/** @brief Saves the results table to clipboard */
void OnClipboardResultsButtonClicked() const;
protected:
std::string streamModelToString() const;
QmitkFitParameterModel * m_InternalModel;
Ui::QmitkFitParameterWidget m_Controls;
};
-/** Helper function to sanatize strings before used in a csv export
+/** Helper function to sanitize strings before used in a csv export
Moved to header in order to be reusabel for other ModelFitUI widgets.*/
std::string SanatizeString(std::string str);
#endif
diff --git a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.cpp
index 4ce6139b67..17704d4149 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.cpp
+++ b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.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 <QColor>
#include <QSize>
#include "mitkExceptionMacro.h"
#include "mitkImage.h"
#include "mitkImageBasedParameterizationDelegate.h"
#include "QmitkInitialValuesModel.h"
QmitkInitialValuesModel::
QmitkInitialValuesModel(QObject* parent) :
QAbstractTableModel(parent), m_modified(false)
{
}
void
QmitkInitialValuesModel::
setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names,
const mitk::ModelTraitsInterface::ParametersType values)
{
if (names.size() != values.size())
{
mitkThrow() <<
"Error. Cannot set initial value model. Passed parameter names vector and default values vector have different size.";
}
emit beginResetModel();
this->m_ParameterNames = names;
this->m_Values = values;
this->m_modified = false;
emit endResetModel();
};
void
QmitkInitialValuesModel::
setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names)
{
mitk::ModelTraitsInterface::ParametersType values;
values.set_size(names.size());
values.fill(0.0);
setInitialValues(names, values);
};
void
QmitkInitialValuesModel::
addInitialParameterImage(const mitk::DataNode* node, mitk::ModelTraitsInterface::ParametersType::size_type paramIndex)
{
if (!node) mitkThrow() << "Try to set a null ptr as initial value source image.";
if (!dynamic_cast<mitk::Image*>(node->GetData())) mitkThrow() << "Error. Passed node does not contain an image.";
emit beginResetModel();
this->m_ParameterImageMap[paramIndex] = node;
emit endResetModel();
};
void QmitkInitialValuesModel::resetInitialParameterImage()
{
emit beginResetModel();
this->m_ParameterImageMap.clear();
emit endResetModel();
};
mitk::ModelTraitsInterface::ParametersType
QmitkInitialValuesModel::
getInitialValues() const
{
return this->m_Values;
};
mitk::InitialParameterizationDelegateBase::Pointer
QmitkInitialValuesModel::
getInitialParametrizationDelegate() const
{
mitk::ImageBasedParameterizationDelegate::Pointer initDelegate = mitk::ImageBasedParameterizationDelegate::New();
initDelegate->SetInitialParameterization(m_Values);
for (const auto& pos : this->m_ParameterImageMap)
{
initDelegate->AddInitialParameterImage(dynamic_cast<mitk::Image*>(pos.second->GetData()), pos.first);
}
return initDelegate.GetPointer();
};
bool
QmitkInitialValuesModel::
hasValidInitialValues() const
{
for (const auto& pos : this->m_ParameterImageMap)
{
if (pos.second.IsNull()) return false;
}
return true;
};
int
QmitkInitialValuesModel::
rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return m_Values.size();
}
int
QmitkInitialValuesModel::
columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return 3;
}
int
QmitkInitialValuesModel::
valueType(const QModelIndex& index) const
{
if (m_ParameterImageMap.find(index.row()) != m_ParameterImageMap.end())
{ //image type
return 1;
}
else
{ //simple scalar
return 0;
}
}
QVariant
QmitkInitialValuesModel::
data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
QVariant result;
if (index.row() < static_cast<int>(m_Values.size()))
{
switch (index.column())
{
case 0:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
result = QVariant(QString::fromStdString(m_ParameterNames[index.row()]));
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Name of the parameter.");
}
break;
case 1:
if (role == Qt::DisplayRole)
{
if (valueType(index) == 1)
{ //image type
result = QVariant("image");
}
else
{ //simple scalar
result = QVariant("scalar");
}
}
else if (role == Qt::EditRole)
{
result = QVariant(valueType(index));
}
else if (role == Qt::ToolTipRole)
{
- result = QVariant("type of the inital value.");
+ result = QVariant("type of the initial value.");
}
break;
case 2:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
const auto& finding = m_ParameterImageMap.find(index.row());
if (finding != m_ParameterImageMap.end())
{ //image type -> return the name
if (finding->second.IsNotNull())
{
result = QVariant(finding->second->GetName().c_str());
}
else
{
result = QVariant("Invalid. Select image.");
}
}
else
{ //simple scalar
result = QVariant(m_Values(index.row()));
}
}
else if (role == Qt::UserRole)
{
result = QVariant(valueType(index));
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Initial values for the parameter.");
}
break;
}
}
return result;
}
Qt::ItemFlags
QmitkInitialValuesModel::
flags(const QModelIndex& index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.row() < static_cast<int>(m_Values.size()))
{
if (index.column() > 0)
{
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
else
{
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
}
return flags;
}
QVariant
QmitkInitialValuesModel::
headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) &&
(Qt::Horizontal == orientation))
{
if (section == 0)
{
return QVariant("Parameters");
}
else if (section == 1)
{
return QVariant("Type");
}
else if (section == 2)
{
return QVariant("Value");
}
}
return QVariant();
}
bool
QmitkInitialValuesModel::
setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid() || index.row() >= static_cast<int>(m_Values.size()) || (index.column() == 0))
{
return false;
}
if (Qt::EditRole == role)
{
emit dataChanged(index, index);
emit beginResetModel();
bool result = false;
if (index.column() == 1)
{
if (value.toInt() == 0)
{
this->m_ParameterImageMap.erase(index.row());
m_modified = true;
result = true;
}
else
{
this->m_ParameterImageMap[index.row()] = nullptr;
m_modified = true;
result = true;
}
}
else
{
if (valueType(index) == 0)
{
m_Values[index.row()] = value.toDouble();
m_modified = true;
result = true;
}
else
{
mitk::DataNode *node = static_cast<mitk::DataNode*>(value.value<void*>());
if (node && dynamic_cast<mitk::Image*>(node->GetData()))
{
this->m_ParameterImageMap[index.row()] = node;
m_modified = true;
result = true;
}
}
}
emit endResetModel();
return result;
}
return false;
};
bool QmitkInitialValuesModel::isModified()
{
return m_modified;
}
diff --git a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h
index c929641cfa..7e08ff6671 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h
@@ -1,93 +1,93 @@
/*============================================================================
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 QmitkInitialValuesModel_h
#define QmitkInitialValuesModel_h
#include <QAbstractTableModel>
#include "mitkSimpleBarrierConstraintChecker.h"
#include "mitkModelTraitsInterface.h"
#include "mitkDataNode.h"
#include "mitkInitialParameterizationDelegateBase.h"
#include "MitkModelFitUIExports.h"
/*!
\class QmitkInitialValuesModel
-Model that handles the definition of inital model values.
+Model that handles the definition of initial model values.
*/
class MITKMODELFITUI_EXPORT QmitkInitialValuesModel : public QAbstractTableModel
{
Q_OBJECT
public:
QmitkInitialValuesModel(QObject* parent = nullptr);
~QmitkInitialValuesModel() override {};
/** Sets the names and the values of the initial parameter set for the model.
@param names List of all possible parameter names. It is assumed that the
index of the list equals the parameter index in the respective fitting model and its parameter values.
@param values Default values to start with.*/
void setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names,
const mitk::ModelTraitsInterface::ParametersType values);
/**@overload
- Convinience method that sets the default initial values always to zero.*/
+ Convenience method that sets the default initial values always to zero.*/
void setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names);
/** Adds an image as a source for the initial value of a parameter.
* @param node Pointer to the image that is the value source.
* @param paramIndex Indicates which parameter is defined by the source image.
* It equals the position of the vector defined by setInitialValues().
* @remark setting an image for an index overwrites the value for this index set by
* SetInitialParameterization.
* @pre paramIndex must be in bound of the initial parametrization vector.
* @pre node must be a valid image instance*/
void addInitialParameterImage(const mitk::DataNode* node, mitk::ModelTraitsInterface::ParametersType::size_type paramIndex);
bool hasValidInitialValues() const;
void resetInitialParameterImage();
/** Returns a pointer to a delegate instance that represents the parameterization of the model.*/
mitk::InitialParameterizationDelegateBase::Pointer getInitialParametrizationDelegate() const;
/** Returns the current set initial values of the model.
* @remark this are only the simpel scalar initial values. If an source image was set, this is missed here.
* Use getInitialParametrizationDelegate() to get everything at once.*/
mitk::ModelTraitsInterface::ParametersType getInitialValues() const;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex& index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
/**Indicates if the content of the model was modified since the data was set via setInitialValues()*/
bool isModified();
private:
int valueType(const QModelIndex& index) const;
mitk::ModelTraitsInterface::ParametersType m_Values;
mitk::ModelTraitsInterface::ParameterNamesType m_ParameterNames;
typedef std::map<mitk::ModelTraitsInterface::ParametersType::size_type, mitk::DataNode::ConstPointer> ImageMapType;
ImageMapType m_ParameterImageMap;
/** Indicates if the data of the model was modified, since the model was set. */
bool m_modified;
};
#endif
diff --git a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp
index 2664f3f934..1cfcd493de 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp
+++ b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp
@@ -1,115 +1,115 @@
/*============================================================================
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 "QmitkParameterFitBackgroundJob.h"
#include "mitkModelFitInfo.h"
void ParameterFitBackgroundJob::OnFitEvent(::itk::Object* caller, const itk::EventObject & event)
{
itk::ProgressEvent progressEvent;
itk::InitializeEvent initEvent;
itk::StartEvent startEvent;
itk::EndEvent endEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast<mitk::ParameterFitImageGeneratorBase*>(caller);
emit JobProgress(castedReporter->GetProgress());
}
else if (initEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Initializing parameter fit generator"));
}
else if (startEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Started fitting process."));
}
else if (endEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Finished fitting process."));
}
}
ParameterFitBackgroundJob::
ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode) : ParameterFitBackgroundJob(generator, fitInfo, parentNode, {})
{
};
ParameterFitBackgroundJob::
ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode, mitk::modelFit::ModelFitResultNodeVectorType additionalRelevantNodes)
{
if (!generator)
{
mitkThrow() << "Cannot create parameter fit background job. Passed fit generator is NULL.";
}
if (!fitInfo)
{
mitkThrow() << "Cannot create parameter fit background job. Passed model traits interface is NULL.";
}
m_Generator = generator;
m_ModelFitInfo = fitInfo;
m_ParentNode = parentNode;
m_AdditionalRelevantNodes = additionalRelevantNodes;
m_spCommand = ::itk::MemberCommand<ParameterFitBackgroundJob>::New();
m_spCommand->SetCallbackFunction(this, &ParameterFitBackgroundJob::OnFitEvent);
m_ObserverID = m_Generator->AddObserver(::itk::AnyEvent(), m_spCommand);
};
mitk::DataNode*
ParameterFitBackgroundJob::
GetParentNode() const
{
return m_ParentNode;
};
mitk::modelFit::ModelFitResultNodeVectorType ParameterFitBackgroundJob::GetAdditionalRelevantNodes() const
{
return m_AdditionalRelevantNodes;
};
ParameterFitBackgroundJob::
~ParameterFitBackgroundJob()
{
m_Generator->RemoveObserver(m_ObserverID);
};
void
ParameterFitBackgroundJob::
run()
{
try
{
emit JobStatusChanged(QString("Started fit session.Generate UID: ")+QString::fromStdString(m_ModelFitInfo->uid));
m_Generator->Generate();
emit JobStatusChanged(QString("Generate result nodes."));
m_Results = mitk::modelFit::CreateResultNodeMap(m_Generator->GetParameterImages(), m_Generator->GetDerivedParameterImages(), m_Generator->GetCriterionImages(), m_Generator->GetEvaluationParameterImages(), m_ModelFitInfo);
emit ResultsAreAvailable(m_Results, this);
}
catch (::std::exception& e)
{
emit Error(QString("Error while fitting data. Details: ")+QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when fitting the data."));
+ emit Error(QString("Unknown error when fitting the data."));
}
emit Finished();
};
diff --git a/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h b/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h
index dd3b29dde3..5f100d497e 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h
@@ -1,49 +1,49 @@
/*============================================================================
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 QmitkSimpleBarrierParametersDelegate_h
#define QmitkSimpleBarrierParametersDelegate_h
/// Toolkit includes.
#include <QStyledItemDelegate>
#include "MitkModelFitUIExports.h"
/** \class QmitkSimpleBarrierParametersDelegate
\brief An item delegate for rendering and editing the parameters relevant for
a barrier constraint. The delegate assumes the following: 1) if the data is requested with the
edit role, it gets a string list of all possible options. 2) if the data is requested with the
display role it gets only a list of all currently selected options.
-If the data is transfered back to the model it contains all selected parameter names in a string list.*/
+If the data is transferred back to the model it contains all selected parameter names in a string list.*/
class MITKMODELFITUI_EXPORT QmitkSimpleBarrierParametersDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
QmitkSimpleBarrierParametersDelegate(QObject* parent = nullptr);
void paint(QPainter* painter, const QStyleOptionViewItem& option
, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option
, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
};
#endif
diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
index e71358bd70..2e453af4a9 100644
--- a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
+++ b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
@@ -1,664 +1,664 @@
/*============================================================================
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 <mitkIOUtil.h>
#include <mitkImageStatisticsHolder.h>
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageConverter.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkAutoCropImageFilter.h>
namespace CppUnit
{
namespace StringHelper
{
template<> inline std::string toString(const mitk::LabelSetImage::LabelValueVectorType& lvs)
{
std::ostringstream stream;
stream << "[";
for (mitk::LabelSetImage::LabelValueVectorType::const_iterator iter = lvs.begin(); iter!=lvs.end(); ++iter)
{
stream << *iter;
if (iter + 1 != lvs.end()) stream << ", ";
}
stream << "]";
return stream.str();
}
template<> inline std::string toString(const std::vector<std::string>& strings)
{
std::ostringstream stream;
stream << "[";
for (std::vector<std::string>::const_iterator iter = strings.begin(); iter != strings.end(); ++iter)
{
stream << *iter;
if (iter + 1 != strings.end()) stream << ", ";
}
stream << "]";
return stream.str();
}
}
}
class mitkLabelSetImageTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkLabelSetImageTestSuite);
MITK_TEST(TestInitialize);
MITK_TEST(TestClone);
MITK_TEST(TestAddLayer);
MITK_TEST(TestGetActiveLabelSet);
MITK_TEST(TestGetActiveLabel);
MITK_TEST(TestInitializeByLabeledImage);
MITK_TEST(TestGetLabel);
MITK_TEST(TestGetLabelValues);
MITK_TEST(TestGetLabelClassNames);
MITK_TEST(TestSetUnlabeledLabelLock);
MITK_TEST(TestGetTotalNumberOfLabels);
MITK_TEST(TestGetNumberOfLabels);
MITK_TEST(TestExistsLabel);
MITK_TEST(TestExistsGroup);
MITK_TEST(TestSetActiveLayer);
MITK_TEST(TestRemoveLayer);
MITK_TEST(TestRemoveLabels);
MITK_TEST(TestEraseLabels);
MITK_TEST(TestMergeLabels);
MITK_TEST(TestCreateLabelMask);
CPPUNIT_TEST_SUITE_END();
private:
mitk::LabelSetImage::Pointer m_LabelSetImage;
int m_LabelAddedEventCount;
int m_LabelModifiedEventCount;
int m_LabelRemovedEventCount;
int m_LabelsChangedEventCount;
int m_GroupAddedEventCount;
int m_GroupModifiedEventCount;
int m_GroupRemovedEventCount;
public:
void setUp() override
{
// Create a new labelset image
m_LabelSetImage = mitk::LabelSetImage::New();
mitk::Image::Pointer regularImage = mitk::Image::New();
unsigned int dimensions[3] = { 96, 128, 52 };
regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
m_LabelSetImage->Initialize(regularImage);
this->ResetEvents();
m_LabelSetImage->AddObserver(mitk::LabelAddedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelAddedEventCount); });
m_LabelSetImage->AddObserver(mitk::LabelModifiedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelModifiedEventCount); });
m_LabelSetImage->AddObserver(mitk::LabelRemovedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelRemovedEventCount); });
m_LabelSetImage->AddObserver(mitk::LabelsChangedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelsChangedEventCount); });
m_LabelSetImage->AddObserver(mitk::GroupAddedEvent(), [this](const itk::EventObject&) { ++(this->m_GroupAddedEventCount); });
m_LabelSetImage->AddObserver(mitk::GroupModifiedEvent(), [this](const itk::EventObject&) { ++(this->m_GroupModifiedEventCount); });
m_LabelSetImage->AddObserver(mitk::GroupRemovedEvent(), [this](const itk::EventObject&) { ++(this->m_GroupRemovedEventCount); });
}
void tearDown() override
{
// Delete LabelSetImage
m_LabelSetImage = nullptr;
}
void ResetEvents()
{
m_LabelAddedEventCount = 0;
m_LabelModifiedEventCount = 0;
m_LabelRemovedEventCount = 0;
m_LabelsChangedEventCount = 0;
m_GroupAddedEventCount = 0;
m_GroupModifiedEventCount = 0;
m_GroupRemovedEventCount = 0;
}
bool CheckEvents(int lAdd, int lMod, int lRem, int lsC, int gAdd, int gMod, int gRem)
{
return m_GroupAddedEventCount == gAdd && m_GroupModifiedEventCount == gMod && m_GroupRemovedEventCount == gRem
&& m_LabelAddedEventCount == lAdd && m_LabelModifiedEventCount == lMod && m_LabelRemovedEventCount == lRem
&& m_LabelsChangedEventCount == lsC;
}
void InitializeTestSegmentation()
{
mitk::Label::Pointer label1 = mitk::Label::New(1, "Label1");
mitk::Label::Pointer label2 = mitk::Label::New(20, "Label2");
mitk::Label::Pointer label22 = mitk::Label::New(22, "Label2");
mitk::Label::Pointer label3 = mitk::Label::New(30, "Label3");
m_LabelSetImage->AddLabel(label1, 0);
m_LabelSetImage->AddLayer({ label2, label22, label3 });
m_LabelSetImage->AddLayer();
this->ResetEvents();
}
void TestInitialize()
{
// LabelSet image should always has the pixel type mitk::Label::PixelType
CPPUNIT_ASSERT_MESSAGE("LabelSetImage has wrong pixel type",
m_LabelSetImage->GetPixelType() == mitk::MakeScalarPixelType<mitk::Label::PixelType>());
mitk::Image::Pointer regularImage = mitk::Image::New();
unsigned int dimensions[3] = { 96, 128, 52 };
regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
mitk::BaseGeometry::Pointer regularImageGeo = regularImage->GetGeometry();
mitk::BaseGeometry::Pointer labelImageGeo = m_LabelSetImage->GetGeometry();
MITK_ASSERT_EQUAL(labelImageGeo, regularImageGeo, "LabelSetImage has wrong geometry");
// By default one layer should be added
CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - number of layers is not one",
m_LabelSetImage->GetNumberOfLayers() == 1);
CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active layer has wrong ID",
m_LabelSetImage->GetActiveLayer() == 0);
CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - no active label should be selected",
m_LabelSetImage->GetActiveLabel() == nullptr);
}
void TestClone()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::Label::Pointer label3 = mitk::Label::New();
label2->SetName("Label3");
label2->SetValue(300);
m_LabelSetImage->AddLabel(label1, 0);
m_LabelSetImage->AddLayer({ label2, label3 });
auto clone = m_LabelSetImage->Clone();
MITK_ASSERT_EQUAL(m_LabelSetImage, clone, "LabelSetImage clone is not equal.");
}
void TestAddLayer()
{
CPPUNIT_ASSERT_MESSAGE("Number of layers is not zero", m_LabelSetImage->GetNumberOfLayers() == 1);
m_LabelSetImage->AddLayer();
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not one",
m_LabelSetImage->GetNumberOfLayers() == 2);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",
m_LabelSetImage->GetActiveLayer() == 0);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - no active label should be selected",
m_LabelSetImage->GetActiveLabel() == nullptr);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0,0,0,0,1,0,0));
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
const auto layerID = m_LabelSetImage->AddLayer({ label1, label2 });
m_LabelSetImage->SetActiveLabel(200);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not two",
m_LabelSetImage->GetNumberOfLayers() == 3);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",
m_LabelSetImage->GetActiveLayer() == layerID);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
m_LabelSetImage->GetActiveLabel()->GetValue() == 200);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 2, 0, 0));
}
void TestGetActiveLabelSet()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::LabelSetImage::ConstLabelVectorType refLayer = { label1, label2 };
unsigned int layerID = m_LabelSetImage->AddLayer(refLayer);
m_LabelSetImage->SetActiveLabel(200);
auto activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == m_LabelSetImage->GetActiveLayer());
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(refLayer, activeLayer, 0.00001, true));
}
void TestGetActiveLabel()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
mitk::Label::PixelType value1 = 1;
label1->SetValue(value1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
mitk::Label::PixelType value2 = 200;
label2->SetValue(value2);
m_LabelSetImage->AddLabel(label1,0);
m_LabelSetImage->AddLabel(label2,0);
m_LabelSetImage->SetActiveLabel(1);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
m_LabelSetImage->GetActiveLabel()->GetValue() == value1);
m_LabelSetImage->SetActiveLabel(value2);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
m_LabelSetImage->GetActiveLabel()->GetValue() == value2);
- CPPUNIT_ASSERT_MESSAGE("Active Label was not correctly retreived with const getter",
+ CPPUNIT_ASSERT_MESSAGE("Active Label was not correctly retrieved with const getter",
const_cast<const mitk::LabelSetImage*>(m_LabelSetImage.GetPointer())->GetActiveLabel()->GetValue() == value2);
}
void TestInitializeByLabeledImage()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 5", m_LabelSetImage->GetNumberOfLabels(0) == 5);
}
void TestGetLabel()
{
mitk::Label::Pointer label1 = mitk::Label::New(1, "Label1");
mitk::Label::Pointer label2 = mitk::Label::New(20,"Label2");
m_LabelSetImage->AddLabel(label1,0);
m_LabelSetImage->AddLayer();
m_LabelSetImage->AddLabel(label2,1);
this->ResetEvents();
CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for active layer",
mitk::Equal(*m_LabelSetImage->GetLabel(1), *label1, 0.0001, true));
CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1",
mitk::Equal(*m_LabelSetImage->GetLabel(20), *label2, 0.0001, true));
// Try to get a non existing label
mitk::Label *unkownLabel = m_LabelSetImage->GetLabel(1000);
CPPUNIT_ASSERT_MESSAGE("Non existing label should be nullptr", unkownLabel == nullptr);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestGetLabelValues()
{
InitializeTestSegmentation();
auto labels = m_LabelSetImage->GetLabelValuesByGroup(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 0",
mitk::LabelSetImage::LabelValueVectorType({ 1 }), labels);
labels = m_LabelSetImage->GetLabelValuesByGroup(1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 1",
mitk::LabelSetImage::LabelValueVectorType({ 20, 22, 30 }), labels);
labels = m_LabelSetImage->GetLabelValuesByGroup(2);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 2",
mitk::LabelSetImage::LabelValueVectorType(), labels);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetLabelValuesByGroup(3), mitk::Exception);
labels = m_LabelSetImage->GetLabelValuesByName(0, "Label1");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for \"Label2\" in group 0",
mitk::LabelSetImage::LabelValueVectorType({ 1 }), labels);
labels = m_LabelSetImage->GetLabelValuesByName(1, "Label2");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for \"Label2\" in group 1",
mitk::LabelSetImage::LabelValueVectorType({ 20, 22 }), labels);
labels = m_LabelSetImage->GetLabelValuesByName(1, "Label3");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for \"Label3\" in group 1",
mitk::LabelSetImage::LabelValueVectorType({ 30 }), labels);
labels = m_LabelSetImage->GetLabelValuesByName(2, "Label1");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 2",
mitk::LabelSetImage::LabelValueVectorType(), labels);
labels = m_LabelSetImage->GetLabelValuesByName(0, "unkown");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for unkown name",
mitk::LabelSetImage::LabelValueVectorType(), labels);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetLabelValuesByName(3,"invalid"), mitk::Exception);
labels = m_LabelSetImage->GetAllLabelValues();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for unkown name",
mitk::LabelSetImage::LabelValueVectorType({1,20,22,30}), labels);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestGetLabelClassNames()
{
InitializeTestSegmentation();
auto names = m_LabelSetImage->GetLabelClassNames();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved",
std::vector<std::string>({ "Label1", "Label2", "Label3"}), names);
names = m_LabelSetImage->GetLabelClassNamesByGroup(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved for group 0",
std::vector<std::string>({ "Label1"}), names);
names = m_LabelSetImage->GetLabelClassNamesByGroup(1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved for group 1",
std::vector<std::string>({"Label2", "Label3" }), names);
names = m_LabelSetImage->GetLabelClassNamesByGroup(2);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved for group 2",
std::vector<std::string>(), names);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetLabelValuesByGroup(3), mitk::Exception);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestSetUnlabeledLabelLock()
{
auto locked = m_LabelSetImage->GetUnlabeledLabelLock();
CPPUNIT_ASSERT_MESSAGE("Wrong UnlabeledLabelLock default state",
locked == false);
m_LabelSetImage->SetUnlabeledLabelLock(true);
locked = m_LabelSetImage->GetUnlabeledLabelLock();
CPPUNIT_ASSERT_MESSAGE("Wrong UnlabeledLabelLock state",
locked == true);
}
void TestGetTotalNumberOfLabels()
{
this->InitializeTestSegmentation();
CPPUNIT_ASSERT_MESSAGE(
"Wrong total number of labels",
m_LabelSetImage->GetTotalNumberOfLabels() == 4);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestGetNumberOfLabels()
{
this->InitializeTestSegmentation();
CPPUNIT_ASSERT_MESSAGE(
"Wrong number of labels in group 0",
m_LabelSetImage->GetNumberOfLabels(0) == 1);
CPPUNIT_ASSERT_MESSAGE(
"Wrong number of labels in group 1",
m_LabelSetImage->GetNumberOfLabels(1) == 3);
CPPUNIT_ASSERT_MESSAGE(
"Wrong number of labels in group 2",
m_LabelSetImage->GetNumberOfLabels(2) == 0);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetNumberOfLabels(3), mitk::Exception);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestExistsLabel()
{
mitk::Label::Pointer label = mitk::Label::New();
label->SetName("Label2");
mitk::Label::PixelType value = 200;
label->SetValue(value);
m_LabelSetImage->AddLayer();
m_LabelSetImage->AddLabel(label,1);
m_LabelSetImage->SetActiveLayer(0);
CPPUNIT_ASSERT_MESSAGE("Existing label was not found", m_LabelSetImage->ExistLabel(value) == true);
CPPUNIT_ASSERT_MESSAGE("Non existing label was found", m_LabelSetImage->ExistLabel(10000) == false);
}
void TestExistsGroup()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
m_LabelSetImage->AddLayer({label1, label2});
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == true);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(1) == true);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(20) == false);
}
void TestSetActiveLayer()
{
// Cache active layer
auto refActiveLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
// Add new layer
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::LabelSetImage::ConstLabelVectorType newlayer = { label1, label2 };
unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
// Set initial layer as active layer
m_LabelSetImage->SetActiveLayer(0);
auto activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(refActiveLayer, activeLayer, 0.00001, true));
// Set previously added layer as active layer
m_LabelSetImage->SetActiveLayer(layerID);
activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(newlayer, activeLayer, 0.00001, true));
// Set a non existing layer as active layer - nothing should change
m_LabelSetImage->SetActiveLayer(10000);
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(newlayer, activeLayer, 0.00001, true));
}
void TestRemoveLayer()
{
// Cache active layer
auto refActiveLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
// Add new layers
m_LabelSetImage->AddLayer();
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::LabelSetImage::ConstLabelVectorType newlayer = { label1, label2 };
unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
m_LabelSetImage->SetActiveLayer(layerID);
auto activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(newlayer, activeLayer, 0.00001, true));
m_LabelSetImage->RemoveGroup(1);
CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
m_LabelSetImage->GetNumberOfLayers() == 2);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(2) == false);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(1) == true);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == true);
m_LabelSetImage->RemoveGroup(1);
activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
m_LabelSetImage->GetNumberOfLayers() == 1);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(1) == false);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == true);
CPPUNIT_ASSERT_MESSAGE("Wrong active layer",
mitk::Equal(refActiveLayer, activeLayer, 0.00001, true));
m_LabelSetImage->RemoveGroup(0);
CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
m_LabelSetImage->GetNumberOfLayers() == 0);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == false);
CPPUNIT_ASSERT_THROW_MESSAGE("GetActiveLayers does not fail although all layer have been removed",
m_LabelSetImage->GetActiveLayer(), mitk::Exception);
}
void TestRemoveLabels()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels(0) == 5);
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
m_LabelSetImage->ExistLabel(3) == true);
m_LabelSetImage->RemoveLabel(1);
std::vector<mitk::Label::PixelType> labelsToBeRemoved;
labelsToBeRemoved.push_back(3);
labelsToBeRemoved.push_back(7);
m_LabelSetImage->RemoveLabels(labelsToBeRemoved);
CPPUNIT_ASSERT_MESSAGE("Wrong number of labels after some have been removed",
m_LabelSetImage->GetNumberOfLabels(0) == 2);
// Values within the image are 0, 1, 3, 5, 6, 7 - New Min / Max value should be 5 / 6
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 were not removed from the image",
m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5);
CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not removed from the image",
m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
}
void TestEraseLabels()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels(0) == 5);
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
m_LabelSetImage->ExistLabel(3) == true);
m_LabelSetImage->EraseLabel(1);
std::vector<mitk::Label::PixelType> labelsToBeErased;
labelsToBeErased.push_back(3);
labelsToBeErased.push_back(7);
m_LabelSetImage->EraseLabels(labelsToBeErased);
CPPUNIT_ASSERT_MESSAGE("Wrong number of labels since none have been removed",
m_LabelSetImage->GetNumberOfLabels(0) == 5);
// Values within the image are 0, 1, 3, 5, 6, 7 - New Min / Max value should be 5 / 6
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 were not erased from the image",
m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5);
CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not erased from the image",
m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
}
void TestMergeLabels()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels(0) == 5);
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
CPPUNIT_ASSERT_MESSAGE("Label with ID 6 does not exist after initialization",
m_LabelSetImage->ExistLabel(6) == true);
// Merge label 7 with label 6. Result should be that label 7 is not present anymore.
m_LabelSetImage->MergeLabel(6, 7);
CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not removed from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
// Count all pixels with value 6 = 507
// Count all pixels with value 7 = 823
// Check if merged label has 507 + 823 = 1330 pixels
CPPUNIT_ASSERT_MESSAGE("Labels were not correctly merged", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 1330);
CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
m_LabelSetImage->ExistLabel(3) == true);
CPPUNIT_ASSERT_MESSAGE("Label with ID 5 does not exist after initialization",
m_LabelSetImage->ExistLabel(5) == true);
// Merge labels 5 and 6 with 3. Result should be that labels 5 and 6 are not present anymore.
std::vector<mitk::Label::PixelType> vectorOfSourcePixelValues{ 5, 6 };
m_LabelSetImage->MergeLabels(3, vectorOfSourcePixelValues);
// Values within the image are 0, 1, 3, 5, 6, 7 - New Max value should be 3
CPPUNIT_ASSERT_MESSAGE("Labels with value 5 and 6 were not removed from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 3);
// Count all pixels with value 3 = 1893
// Count all pixels with value 5 = 2143
// Count all pixels with value 6 = 1330
// Check if merged label has 1893 + 2143 + 1330 = 5366 pixels
CPPUNIT_ASSERT_MESSAGE("Labels were not correctly merged", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 5366);
}
void TestCreateLabelMask()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
auto labelMask = mitk::CreateLabelMask(m_LabelSetImage,6);
mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
cropFilter->SetInput(labelMask);
cropFilter->SetBackgroundValue(0);
cropFilter->SetMarginFactor(1.15);
cropFilter->Update();
auto maskImage = cropFilter->GetOutput();
// Count all pixels with value 6 = 507
CPPUNIT_ASSERT_MESSAGE("Label mask not correctly created", maskImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 507);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImage)
diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
index ad1fcb89d7..c0e2cba845 100644
--- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
+++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
@@ -1,694 +1,694 @@
/*============================================================================
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 __mitkDICOMSegmentationIO__cpp
#define __mitkDICOMSegmentationIO__cpp
#include "mitkDICOMSegmentationIO.h"
#include "mitkDICOMSegIOMimeTypes.h"
#include "mitkDICOMSegmentationConstants.h"
#include <mitkDICOMDCMTKTagScanner.h>
#include <mitkDICOMIOHelper.h>
#include <mitkDICOMProperty.h>
#include <mitkIDICOMTagsOfInterest.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkLocaleSwitch.h>
#include <mitkPropertyNameHelper.h>
// itk
#include <itkThresholdImageFilter.h>
// dcmqi
#include <dcmqi/ImageSEGConverter.h>
// us
#include <usGetModuleContext.h>
#include <usModuleContext.h>
namespace mitk
{
DICOMSegmentationIO::DICOMSegmentationIO()
: AbstractFileIO(LabelSetImage::GetStaticNameOfClass(),
mitk::MitkDICOMSEGIOMimeTypes::DICOMSEG_MIMETYPE_NAME(),
"DICOM Segmentation")
{
AbstractFileWriter::SetRanking(10);
AbstractFileReader::SetRanking(10);
this->RegisterService();
}
std::vector<mitk::DICOMTagPath> DICOMSegmentationIO::GetDICOMTagsOfInterest()
{
std::vector<mitk::DICOMTagPath> result;
result.emplace_back(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_LABEL_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH());
return result;
}
IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const
{
if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
return Unsupported;
// Check if the input file is a segmentation
const LabelSetImage *input = dynamic_cast<const LabelSetImage *>(this->GetInput());
if (input)
{
if ((input->GetDimension() != 3))
{
MITK_INFO << "DICOM segmentation writer is tested only with 3D images, sorry.";
return Unsupported;
}
// Check if input file has dicom information for the referenced image (original DICOM image, e.g. CT) Still necessary, see write()
mitk::StringLookupTableProperty::Pointer dicomFilesProp =
dynamic_cast<mitk::StringLookupTableProperty *>(input->GetProperty("referenceFiles").GetPointer());
if (dicomFilesProp.IsNotNull())
return Supported;
}
return Unsupported;
}
void DICOMSegmentationIO::Write()
{
ValidateOutputLocation();
mitk::LocaleSwitch localeSwitch("C");
LocalFile localFile(this);
const std::string path = localFile.GetFileName();
auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
if (input == nullptr)
mitkThrow() << "Cannot write non-image data";
// Get DICOM information from referenced image
vector<std::unique_ptr<DcmDataset>> dcmDatasetsSourceImage;
std::unique_ptr<DcmFileFormat> readFileFormat(new DcmFileFormat());
try
{
// TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the
// property list
mitk::StringLookupTableProperty::Pointer filesProp =
dynamic_cast<mitk::StringLookupTableProperty *>(input->GetProperty("referenceFiles").GetPointer());
if (filesProp.IsNull())
{
mitkThrow() << "No property with dicom file path.";
return;
}
StringLookupTable filesLut = filesProp->GetValue();
const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable();
for (const auto &it : lookUpTableMap)
{
const char *fileName = (it.second).c_str();
if (readFileFormat->loadFile(fileName, EXS_Unknown).good())
{
std::unique_ptr<DcmDataset> readDCMDataset(readFileFormat->getAndRemoveDataset());
dcmDatasetsSourceImage.push_back(std::move(readDCMDataset));
}
}
}
catch (const std::exception &e)
{
MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl;
return;
}
// Iterate over all layers. For each a dcm file will be generated
for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer)
{
vector<itkInternalImageType::Pointer> segmentations;
try
{
auto mitkLayerImage = input->GetGroupImage(layer);
// Cast mitk layer image to itk
ImageToItk<itkInputImageType>::Pointer imageToItkFilter = ImageToItk<itkInputImageType>::New();
imageToItkFilter->SetInput(mitkLayerImage);
// Cast from original itk type to dcmqi input itk image type
typedef itk::CastImageFilter<itkInputImageType, itkInternalImageType> castItkImageFilterType;
castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New();
castFilter->SetInput(imageToItkFilter->GetOutput());
castFilter->Update();
itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput();
itkLabelImage->DisconnectPipeline();
// Iterate over all labels. For each label a segmentation image will be created
auto labelSet = input->GetConstLabelsByValue(input->GetLabelValuesByGroup(layer));
for (const auto& label : labelSet)
{
- // Thresold over the image with the given label value
+ // Threshold over the image with the given label value
itk::ThresholdImageFilter<itkInternalImageType>::Pointer thresholdFilter =
itk::ThresholdImageFilter<itkInternalImageType>::New();
thresholdFilter->SetInput(itkLabelImage);
thresholdFilter->ThresholdOutside(label->GetValue(), label->GetValue());
thresholdFilter->SetOutsideValue(0);
thresholdFilter->Update();
itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput();
segmentImage->DisconnectPipeline();
segmentations.push_back(segmentImage);
}
}
catch (const itk::ExceptionObject &e)
{
MITK_ERROR << e.GetDescription() << endl;
return;
}
// Create segmentation meta information
const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer);
MITK_INFO << "Writing image: " << path << std::endl;
try
{
//TODO is there a better way? Interface expects a vector of raw pointer.
vector<DcmDataset*> rawVecDataset;
for (const auto& dcmDataSet : dcmDatasetsSourceImage)
rawVecDataset.push_back(dcmDataSet.get());
// Convert itk segmentation images to dicom image
std::unique_ptr<dcmqi::ImageSEGConverter> converter = std::make_unique<dcmqi::ImageSEGConverter>();
std::unique_ptr<DcmDataset> result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile, false));
// Write dicom file
DcmFileFormat dcmFileFormat(result.get());
std::string filePath = path.substr(0, path.find_last_of("."));
// If there is more than one layer, we have to write more than 1 dicom file
if (input->GetNumberOfLayers() != 1)
filePath = filePath + std::to_string(layer) + ".dcm";
else
filePath = filePath + ".dcm";
dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit);
}
catch (const std::exception &e)
{
MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl;
return;
}
} // Write a dcm file for the next layer
}
IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const
{
if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
return Unsupported;
const std::string fileName = this->GetLocalFileName();
DcmFileFormat dcmFileFormat;
OFCondition status = dcmFileFormat.loadFile(fileName.c_str());
if (status.bad())
return Unsupported;
OFString modality;
if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good())
{
if (modality.compare("SEG") == 0)
return Supported;
else
return Unsupported;
}
return Unsupported;
}
std::vector<BaseData::Pointer> DICOMSegmentationIO::DoRead()
{
mitk::LocaleSwitch localeSwitch("C");
LabelSetImage::Pointer labelSetImage;
std::vector<BaseData::Pointer> result;
const std::string path = this->GetLocalFileName();
MITK_INFO << "loading " << path << std::endl;
if (path.empty())
mitkThrow() << "Empty filename in mitk::ItkImageIO ";
try
{
// Get the dcm data set from file path
DcmFileFormat dcmFileFormat;
OFCondition status = dcmFileFormat.loadFile(path.c_str());
if (status.bad())
mitkThrow() << "Can't read the input file!";
DcmDataset *dataSet = dcmFileFormat.getDataset();
if (dataSet == nullptr)
mitkThrow() << "Can't read data from input file!";
//=============================== dcmqi part ====================================
// Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo)
std::unique_ptr<dcmqi::ImageSEGConverter> converter = std::make_unique<dcmqi::ImageSEGConverter>();
pair<map<unsigned, itkInternalImageType::Pointer>, string> dcmqiOutput =
converter->dcmSegmentation2itkimage(dataSet);
map<unsigned, itkInternalImageType::Pointer> segItkImages = dcmqiOutput.first;
dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str());
metaInfo.read();
MITK_INFO << "Input " << metaInfo.getJSONOutputAsString();
//===============================================================================
// Get the label information from segment attributes for each itk image
vector<map<unsigned, dcmqi::SegmentAttributes *>>::const_iterator segmentIter =
metaInfo.segmentsAttributesMappingList.begin();
// For each itk image add a layer to the LabelSetImage output
for (auto &element : segItkImages)
{
// Get the labeled image and cast it to mitkImage
typedef itk::CastImageFilter<itkInternalImageType, itkInputImageType> castItkImageFilterType;
castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New();
castFilter->SetInput(element.second);
castFilter->Update();
Image::Pointer layerImage;
CastToMitkImage(castFilter->GetOutput(), layerImage);
// Get pixel value of the label
itkInternalImageType::ValueType segValue = 1;
typedef itk::ImageRegionIterator<const itkInternalImageType> IteratorType;
// Iterate over the image to find the pixel value of the label
IteratorType iter(element.second, element.second->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
itkInputImageType::PixelType value = iter.Get();
if (value != LabelSetImage::UNLABELED_VALUE)
{
segValue = value;
break;
}
++iter;
}
// Get Segment information map
map<unsigned, dcmqi::SegmentAttributes *> segmentMap = (*segmentIter);
map<unsigned, dcmqi::SegmentAttributes *>::const_iterator segmentMapIter = (*segmentIter).begin();
dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second;
OFString labelName;
if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr)
{
segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName);
if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr)
{
OFString modifier;
segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier);
labelName.append(" (").append(modifier).append(")");
}
}
else
{
labelName = std::to_string(segmentAttribute->getLabelID()).c_str();
if (labelName.empty())
labelName = "Unnamed";
}
float tmp[3] = { 0.0, 0.0, 0.0 };
if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr)
{
tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0;
tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0;
tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0;
}
Label *newLabel = nullptr;
// If labelSetImage do not exists (first image)
if (labelSetImage.IsNull())
{
// Initialize the labelSetImage with the read image
labelSetImage = LabelSetImage::New();
labelSetImage->InitializeByLabeledImage(layerImage);
// Already a label was generated, so set the information to this
newLabel = labelSetImage->GetActiveLabel();
newLabel->SetName(labelName.c_str());
newLabel->SetColor(Color(tmp));
newLabel->SetValue(segValue);
}
else
{
// Add a new layer to the labelSetImage. Background label is set automatically
labelSetImage->AddLayer(layerImage);
// Add new label
newLabel = new Label;
newLabel->SetName(labelName.c_str());
newLabel->SetColor(Color(tmp));
newLabel->SetValue(segValue);
labelSetImage->AddLabel(newLabel, labelSetImage->GetActiveLayer());
}
// Add some more label properties
this->SetLabelProperties(newLabel, segmentAttribute);
++segmentIter;
}
labelSetImage->SetAllLabelsVisible(true);
// Add some general DICOM Segmentation properties
mitk::IDICOMTagsOfInterest *toiSrv = DICOMIOHelper::GetTagsOfInterestService();
auto tagsOfInterest = toiSrv->GetTagsOfInterest();
DICOMTagPathList tagsOfInterestList;
for (const auto &tag : tagsOfInterest)
{
tagsOfInterestList.push_back(tag.first);
}
mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New();
scanner->SetInputFiles({ GetInputLocation() });
scanner->AddTagPaths(tagsOfInterestList);
scanner->Scan();
mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList();
if (frames.empty())
{
MITK_ERROR << "Error reading the DICOM Seg file" << std::endl;
return result;
}
auto findings = DICOMIOHelper::ExtractPathsOfInterest(tagsOfInterestList, frames);
DICOMIOHelper::SetProperties(labelSetImage, findings);
// Set active layer to the first layer of the labelset image
if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0)
labelSetImage->SetActiveLayer(0);
}
catch (const std::exception &e)
{
MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what();
return result;
}
catch (...)
{
MITK_ERROR << "An error occurred in dcmqi while reading the DICOM Seg file";
return result;
}
result.push_back(labelSetImage.GetPointer());
return result;
}
const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer)
{
const mitk::LabelSetImage *image = dynamic_cast<const mitk::LabelSetImage *>(this->GetInput());
const std::string output;
dcmqi::JSONSegmentationMetaInformationHandler handler;
// 1. Metadata attributes that will be listed in the resulting DICOM SEG object
std::string contentCreatorName;
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(),
contentCreatorName))
contentCreatorName = "MITK";
handler.setContentCreatorName(contentCreatorName);
std::string clinicalTrailSeriesId;
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(),
clinicalTrailSeriesId))
clinicalTrailSeriesId = "Session 1";
handler.setClinicalTrialSeriesID(clinicalTrailSeriesId);
std::string clinicalTrialTimePointID;
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(),
clinicalTrialTimePointID))
clinicalTrialTimePointID = "0";
handler.setClinicalTrialTimePointID(clinicalTrialTimePointID);
std::string clinicalTrialCoordinatingCenterName = "";
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(),
clinicalTrialCoordinatingCenterName))
clinicalTrialCoordinatingCenterName = "Unknown";
handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName);
std::string seriesDescription;
if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription))
seriesDescription = "MITK Segmentation";
handler.setSeriesDescription(seriesDescription);
handler.setSeriesNumber("0" + std::to_string(layer));
handler.setInstanceNumber("1");
handler.setBodyPartExamined("");
auto labelSet = image->GetConstLabelsByValue(image->GetLabelValuesByGroup(layer));
for (const auto& label : labelSet)
{
if (label != nullptr)
{
TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str()));
TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str()));
TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str()));
TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str()));
TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str()));
TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str()));
TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str()));
TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str()));
dcmqi::SegmentAttributes *segmentAttribute = nullptr;
if (segmentNumberProp->GetValue() == "")
{
MITK_ERROR << "Something went wrong with the label ID.";
}
else
{
int labelId = std::stoi(segmentNumberProp->GetValue());
segmentAttribute = handler.createAndGetNewSegment(labelId);
}
if (segmentAttribute != nullptr)
{
segmentAttribute->setSegmentLabel(segmentLabelProp->GetValueAsString());
segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString());
segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString());
segmentAttribute->setSegmentAlgorithmName("MITK Segmentation");
if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr &&
segmentCategoryCodeMeaningProp != nullptr)
segmentAttribute->setSegmentedPropertyCategoryCodeSequence(
segmentCategoryCodeValueProp->GetValueAsString(),
segmentCategoryCodeSchemeProp->GetValueAsString(),
segmentCategoryCodeMeaningProp->GetValueAsString());
else
// some default values
segmentAttribute->setSegmentedPropertyCategoryCodeSequence(
"M-01000", "SRT", "Morphologically Altered Structure");
if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr &&
segmentTypeCodeMeaningProp != nullptr)
{
segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(),
segmentTypeCodeSchemeProp->GetValueAsString(),
segmentTypeCodeMeaningProp->GetValueAsString());
handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString());
}
else
{
// some default values
segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass");
handler.setBodyPartExamined("Mass");
}
if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr &&
segmentModifierCodeMeaningProp != nullptr)
segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence(
segmentModifierCodeValueProp->GetValueAsString(),
segmentModifierCodeSchemeProp->GetValueAsString(),
segmentModifierCodeMeaningProp->GetValueAsString());
Color color = label->GetColor();
segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255);
}
}
}
return handler.getJSONOutputAsString();
}
void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute)
{
// Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique
// within the Segmentation instance in which it is created
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(),
TemporoSpatialStringProperty::New(std::to_string(label->GetValue())));
// Segment Label: User-defined label identifying this segment.
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(),
TemporoSpatialStringProperty::New(label->GetName()));
// Segment Algorithm Type: Type of algorithm used to generate the segment.
if (!segmentAttribute->getSegmentAlgorithmType().empty())
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(),
TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType()));
// Add Segmented Property Category Code Sequence tags
auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence();
if (categoryCodeSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
categoryCodeSequence->getCodeValue(codeValue);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
categoryCodeSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
categoryCodeSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
// Add Segmented Property Type Code Sequence tags
auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence();
if (typeCodeSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
typeCodeSequence->getCodeValue(codeValue);
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
typeCodeSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
typeCodeSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
// Add Segmented Property Type Modifier Code Sequence tags
auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence();
if (modifierCodeSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
modifierCodeSequence->getCodeValue(codeValue);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
modifierCodeSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
modifierCodeSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
// Add Atomic RegionSequence tags
auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence();
if (atomicRegionSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
atomicRegionSequence->getCodeValue(codeValue);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
atomicRegionSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
atomicRegionSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
}
DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); }
} // namespace
#endif //__mitkDICOMSegmentationIO__cpp
diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h
index 6720ccc7c7..83823ad089 100644
--- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h
+++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h
@@ -1,70 +1,70 @@
/*============================================================================
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 mitkDICOMSegmentationIO_h
#define mitkDICOMSegmentationIO_h
#include <mitkAbstractFileIO.h>
#include <mitkLabelSetImage.h>
#include <mitkDICOMTagsOfInterestAddHelper.h>
#include <dcmqi/JSONSegmentationMetaInformationHandler.h>
#include <memory>
namespace mitk
{
/**
* Read and Writes a LabelSetImage to a dcm file
* @ingroup Process
*/
class DICOMSegmentationIO : public mitk::AbstractFileIO
{
public:
typedef mitk::LabelSetImage InputType;
typedef itk::Image<unsigned short, 3> itkInputImageType;
typedef itk::Image<short, 3> itkInternalImageType;
DICOMSegmentationIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
static std::vector<mitk::DICOMTagPath> GetDICOMTagsOfInterest();
protected:
/**
* @brief Reads a number of DICOM segmentation from the file system
* @return a vector of mitk::LabelSetImages
- * @throws throws an mitk::Exception if an error ocurrs
+ * @throws throws an mitk::Exception if an error occurs
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
private:
DICOMSegmentationIO *IOClone() const override;
// -------------- DICOMSegmentationIO specific functions -------------
const std::string CreateMetaDataJsonFile(int layer);
void SetLabelProperties(Label *label, dcmqi::SegmentAttributes *segmentAttribute);
};
} // end of namespace mitk
#endif
diff --git a/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
index 0a99aebd99..3ae393eccf 100644
--- a/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
+++ b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
@@ -1,59 +1,59 @@
/*============================================================================
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 mitkLegacyLabelSetImageIO_h
#define mitkLegacyLabelSetImageIO_h
#include <mitkAbstractFileReader.h>
#include <mitkLabelSetImage.h>
namespace mitk
{
/**
* Writes a LabelSetImage to a file.
* mitk::Identifiable UID is supported and will be serialized.
* @ingroup Process
*/
// The export macro should be removed. Currently, the unit
// tests directly instantiate this class.
class LegacyLabelSetImageIO : public mitk::AbstractFileReader
{
public:
typedef mitk::LabelSetImage InputType;
LegacyLabelSetImageIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetConfidenceLevel() const override;
protected:
/**
* @brief Reads a number of mitk::LabelSetImages from the file system
* @return a vector of mitk::LabelSetImages
- * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
+ * @throws throws an mitk::Exception if an error occurs during parsing the nrrd header
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
// Fills the m_DefaultMetaDataKeys vector with default values
virtual void InitializeDefaultMetaDataKeys();
private:
LegacyLabelSetImageIO *Clone() const override;
std::vector<std::string> m_DefaultMetaDataKeys;
};
} // end of namespace mitk
#endif
diff --git a/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
index b2a5757143..397c26b8c7 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
+++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
@@ -1,64 +1,64 @@
/*============================================================================
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 mitkMultiLabelSegmentationIO_h
#define mitkMultiLabelSegmentationIO_h
#include <mitkAbstractFileIO.h>
#include <mitkLabelSetImage.h>
namespace mitk
{
/**
* Writes a LabelSetImage to a file.
* mitk::Identifiable UID is supported and will be serialized.
* @ingroup Process
*/
// The export macro should be removed. Currently, the unit
// tests directly instantiate this class.
class MultiLabelSegmentationIO : public mitk::AbstractFileIO
{
public:
typedef mitk::LabelSetImage InputType;
MultiLabelSegmentationIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
protected:
/**
* @brief Reads a number of mitk::LabelSetImages from the file system
* @return a vector of mitk::LabelSetImages
- * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
+ * @throws throws an mitk::Exception if an error occurs during parsing the nrrd header
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
// Fills the m_DefaultMetaDataKeys vector with default values
virtual void InitializeDefaultMetaDataKeys();
private:
MultiLabelSegmentationIO *IOClone() const override;
std::vector<std::string> m_DefaultMetaDataKeys;
};
} // end of namespace mitk
#endif
diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
index 3f54523643..194fc89387 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
@@ -1,146 +1,146 @@
/*============================================================================
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 "mitkMultilabelIOMimeTypes.h"
#include <mitkIOMimeTypes.h>
#include <mitkLog.h>
#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
#include <itkNrrdImageIO.h>
#include "itkMetaDataObject.h"
mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::MitkSegmentationTaskListMimeType()
: CustomMimeType(SEGMENTATIONTASKLIST_MIMETYPE_NAME())
{
this->AddExtension("json");
this->SetCategory("MITK Segmentation Task List");
this->SetComment("MITK Segmentation Task List");
}
bool mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::AppliesTo(const std::string& path) const
{
bool result = CustomMimeType::AppliesTo(path);
if (!std::filesystem::exists(path)) // T18572
return result;
std::ifstream file(path);
if (!file.is_open())
return false;
auto json = nlohmann::json::parse(file, nullptr, false);
if (json.is_discarded() || !json.is_object())
return false;
if ("MITK Segmentation Task List" != json.value("FileFormat", ""))
return false;
if (1 != json.value<int>("Version", 0))
return false;
return true;
}
mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType* mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::Clone() const
{
return new MitkSegmentationTaskListMimeType(*this);
}
mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE()
{
return MitkSegmentationTaskListMimeType();
}
std::string mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".segmentationtasklist";
}
mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::LegacyLabelSetMimeType()
: CustomMimeType(LEGACYLABELSET_MIMETYPE_NAME())
{
this->AddExtension("nrrd");
this->SetCategory("MITK LabelSetImage");
this->SetComment("MITK LabelSetImage (legacy format)");
}
bool mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::AppliesTo(const std::string& path) const
{
bool canRead = CustomMimeType::AppliesTo(path);
if (!std::filesystem::exists(path)) // T18572
return canRead;
if (!canRead)
{
return false;
}
std::string value("");
try
{
std::ifstream file(path);
if (!file.is_open())
return false;
itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
io->SetFileName(path);
io->ReadImageInformation();
itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
}
catch(const std::exception& e)
{
MITK_DEBUG << "Error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path <<"; Error: " << e.what();
}
catch(...)
{
- MITK_DEBUG << "Unkown error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path;
+ MITK_DEBUG << "Unknown error while try to analyze NRRD file for LegacyLabelSetMimeType. File: " << path;
}
if (value.compare("org.mitk.image.multilabel") != 0)
{
return false;
}
return true;
}
mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType* mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::Clone() const
{
return new LegacyLabelSetMimeType(*this);
}
mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE()
{
return LegacyLabelSetMimeType();
}
std::string mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".legacylabelsetimage";
}
std::vector<mitk::CustomMimeType*> mitk::MitkMultilabelIOMimeTypes::Get()
{
std::vector<CustomMimeType*> mimeTypes;
mimeTypes.push_back(SEGMENTATIONTASKLIST_MIMETYPE().Clone());
mimeTypes.push_back(LEGACYLABELSET_MIMETYPE().Clone());
return mimeTypes;
}
diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h
index 649affabc6..03bcec65fe 100644
--- a/Modules/Multilabel/mitkLabel.h
+++ b/Modules/Multilabel/mitkLabel.h
@@ -1,115 +1,115 @@
/*============================================================================
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 mitkLabel_h
#define mitkLabel_h
#include "MitkMultilabelExports.h"
#include <mitkColorProperty.h>
#include <mitkPropertyList.h>
#include <mitkPoint.h>
#include <mitkVector.h>
namespace mitk
{
//##
//##Documentation
//## @brief A data structure describing a label.
//## @ingroup Data
//##
class MITKMULTILABEL_EXPORT Label : public PropertyList
{
public:
mitkClassMacro(Label, mitk::PropertyList);
typedef unsigned short PixelType;
itkNewMacro(Self);
mitkNewMacro2Param(Self, PixelType, const std::string&);
/// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535
static const PixelType MAX_LABEL_VALUE;
//** Value indicating pixels that are not labeled at all.*/
static constexpr PixelType UNLABELED_VALUE = 0;
void SetLocked(bool locked);
bool GetLocked() const;
void SetVisible(bool visible);
bool GetVisible() const;
void SetOpacity(float opacity);
float GetOpacity() const;
void SetName(const std::string &name);
std::string GetName() const;
std::string GetTrackingID() const;
void SetCenterOfMassIndex(const mitk::Point3D &center);
mitk::Point3D GetCenterOfMassIndex() const;
void SetCenterOfMassCoordinates(const mitk::Point3D &center);
mitk::Point3D GetCenterOfMassCoordinates() const;
void SetColor(const mitk::Color &);
const mitk::Color &GetColor() const;
void SetValue(PixelType pixelValue);
PixelType GetValue() const;
void SetLayer(unsigned int layer);
unsigned int GetLayer() const;
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
using itk::Object::Modified;
void Modified() { Superclass::Modified(); }
Label();
Label(PixelType value, const std::string& name);
~Label() override;
protected:
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
Label(const Label &other);
private:
itk::LightObject::Pointer InternalClone() const override;
};
using LabelVector = std::vector<Label::Pointer>;
using ConstLabelVector = std::vector<Label::ConstPointer>;
/**
- * @brief Equal A function comparing two labels for beeing equal in data
+ * @brief Equal A function comparing two labels for being equal in data
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - Lebel equality via Equal-PropetyList
*
* @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
*/
MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide,
const mitk::Label &rightHandSide,
ScalarType eps,
bool verbose);
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp
index 21d7cc19d7..7a4aa72a47 100644
--- a/Modules/Multilabel/mitkLabelSetImage.cpp
+++ b/Modules/Multilabel/mitkLabelSetImage.cpp
@@ -1,1750 +1,1750 @@
/*============================================================================
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 "mitkLabelSetImage.h"
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkImagePixelWriteAccessor.h>
#include <mitkPadImageFilter.h>
#include <mitkDICOMSegmentationPropertyHelper.h>
#include <mitkDICOMQIPropertyHelper.h>
#include <mitkNodePredicateGeometry.h>
#include <itkLabelGeometryImageFilter.h>
#include <itkCommand.h>
#include <itkBinaryFunctorImageFilter.h>
namespace mitk
{
template <typename ImageType>
void ClearBufferProcessing(ImageType* itkImage)
{
itkImage->FillBuffer(0);
}
void ClearImageBuffer(mitk::Image* image)
{
if (image->GetDimension() == 4)
{ //remark: this extra branch was added, because LabelSetImage instances can be
//dynamic (4D), but AccessByItk by support only supports 2D and 3D.
//The option to change the CMake default dimensions for AccessByItk was
//dropped (for details see discussion in T28756)
AccessFixedDimensionByItk(image, ClearBufferProcessing, 4);
}
else
{
AccessByItk(image, ClearBufferProcessing);
}
}
}
const mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::UNLABELED_VALUE = 0;
mitk::LabelSetImage::LabelSetImage()
: mitk::Image(), m_ActiveLabelValue(0), m_UnlabeledLabelLock(false), m_ActiveLayer(0), m_activeLayerInvalid(false)
{
m_LookupTable = mitk::LookupTable::New();
m_LookupTable->SetType(mitk::LookupTable::MULTILABEL);
// Add some DICOM Tags as properties to segmentation image
DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
}
mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage &other)
: Image(other),
m_ActiveLabelValue(other.m_ActiveLabelValue),
m_LookupTable(other.m_LookupTable->Clone()),
m_UnlabeledLabelLock(other.m_UnlabeledLabelLock),
m_ActiveLayer(other.GetActiveLayer()),
m_activeLayerInvalid(false)
{
GroupIndexType i = 0;
for (auto groupImage : other.m_LayerContainer)
{
this->AddLayer(groupImage->Clone(), other.GetConstLabelsByValue(other.GetLabelValuesByGroup(i)));
i++;
}
m_Groups = other.m_Groups;
// Add some DICOM Tags as properties to segmentation image
DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
}
void mitk::LabelSetImage::Initialize(const mitk::Image *other)
{
mitk::PixelType pixelType(mitk::MakeScalarPixelType<LabelSetImage::PixelType>());
if (other->GetDimension() == 2)
{
const unsigned int dimensions[] = {other->GetDimension(0), other->GetDimension(1), 1};
Superclass::Initialize(pixelType, 3, dimensions);
}
else
{
Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions());
}
auto originalGeometry = other->GetTimeGeometry()->Clone();
this->SetTimeGeometry(originalGeometry);
// initialize image memory to zero
ClearImageBuffer(this);
// Transfer some general DICOM properties from the source image to derived image (e.g. Patient information,...)
DICOMQIPropertyHelper::DeriveDICOMSourceProperties(other, this);
- // Add a inital LabelSet ans corresponding image data to the stack
+ // Add an initial LabelSet and corresponding image data to the stack
if (this->GetNumberOfLayers() == 0)
{
AddLayer();
}
}
mitk::LabelSetImage::~LabelSetImage()
{
for (auto [value, label] : m_LabelMap)
{
this->ReleaseLabel(label);
}
m_LabelMap.clear();
}
unsigned int mitk::LabelSetImage::GetActiveLayer() const
{
if (m_LayerContainer.size() == 0) mitkThrow() << "Cannot return active layer index. No layer is available.";
return m_ActiveLayer;
}
unsigned int mitk::LabelSetImage::GetNumberOfLayers() const
{
return m_LayerContainer.size();
}
void mitk::LabelSetImage::RemoveGroup(GroupIndexType indexToDelete)
{
if (!this->ExistGroup(indexToDelete)) mitkThrow() << "Cannot remove group. Group does not exist. Invalid group index: "<<indexToDelete;
const auto activeIndex = GetActiveLayer();
auto newActiveIndex = activeIndex;
auto newActiveIndexBeforeDeletion = activeIndex;
//determine new active group index (after the group will be removed);
if (indexToDelete < activeIndex)
{ //lower the index because position in m_LayerContainer etc has changed
newActiveIndex = activeIndex-1;
}
else if (indexToDelete == activeIndex)
{
if (this->GetNumberOfLayers() == 1)
{ //last layer is about to be deleted
newActiveIndex = 0;
}
else
{
//we have to add/subtract one more because we have not removed the layer yet, thus the group count is to 1 high.
newActiveIndex = indexToDelete+1 < GetNumberOfLayers() ? indexToDelete : GetNumberOfLayers() - 2;
newActiveIndexBeforeDeletion = indexToDelete + 1 < GetNumberOfLayers() ? indexToDelete+1 : indexToDelete -1;
}
}
if (activeIndex == indexToDelete)
{
// we are deleting the active layer, it should not be copied back into the vector
m_activeLayerInvalid = true;
//copy the image content of the upcoming new active layer;
SetActiveLayer(newActiveIndexBeforeDeletion);
}
auto relevantLabels = m_GroupToLabelMap[indexToDelete];
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
// remove labels of group
for (auto labelValue : relevantLabels)
{
auto label = m_LabelMap[labelValue];
this->ReleaseLabel(label);
m_LabelToGroupMap.erase(labelValue);
m_LabelMap.erase(labelValue);
this->InvokeEvent(LabelRemovedEvent(labelValue));
}
// remove the group entries in the maps and the image.
m_Groups.erase(m_Groups.begin() + indexToDelete);
m_GroupToLabelMap.erase(m_GroupToLabelMap.begin() + indexToDelete);
m_LayerContainer.erase(m_LayerContainer.begin() + indexToDelete);
}
//update old indexes in m_GroupToLabelMap to new layer indexes
for (auto& element : m_LabelToGroupMap)
{
if (element.second > indexToDelete) element.second = element.second -1;
}
//correct active layer index
m_ActiveLayer = newActiveIndex;
this->InvokeEvent(LabelsChangedEvent(relevantLabels));
this->InvokeEvent(GroupRemovedEvent(indexToDelete));
this->Modified();
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const LabelVectorType& labels)
{
LabelValueVectorType result;
for (auto label : labels)
{
result.emplace_back(label->GetValue());
}
return result;
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels)
{
LabelValueVectorType result;
for (auto label : labels)
{
result.emplace_back(label->GetValue());
}
return result;
}
mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::ConvertLabelVectorConst(const LabelVectorType& labels)
{
ConstLabelVectorType result(labels.begin(), labels.end());
return result;
};
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetAllLabelValues() const
{
LabelValueVectorType result;
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(value);
}
return result;
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetUsedLabelValues() const
{
LabelValueVectorType result = { UNLABELED_VALUE };
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(value);
}
return result;
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(ConstLabelVector labels)
{
mitk::Image::Pointer newImage = mitk::Image::New();
newImage->Initialize(this->GetPixelType(),
this->GetDimension(),
this->GetDimensions(),
this->GetImageDescriptor()->GetNumberOfChannels());
newImage->SetTimeGeometry(this->GetTimeGeometry()->Clone());
ClearImageBuffer(newImage);
return this->AddLayer(newImage, labels);
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(mitk::Image* layerImage, ConstLabelVector labels)
{
GroupIndexType newGroupID = m_Groups.size();
if (nullptr == layerImage)
mitkThrow() << "Cannot add group. Passed group image is nullptr.";
bool equalGeometries = Equal(
*(this->GetTimeGeometry()),
*(layerImage->GetTimeGeometry()),
NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION,
NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION,
false);
if (!equalGeometries)
mitkThrow() << "Cannot add group. Passed group image has not the same geometry like segmentation.";
if (layerImage->GetPixelType() != MakePixelType<LabelValueType, LabelValueType, 1>())
mitkThrow() << "Cannot add group. Passed group image has incorrect pixel type. Only LabelValueType is supported. Invalid pixel type: "<< layerImage->GetPixelType().GetTypeAsString();
// push a new working image for the new layer
m_LayerContainer.push_back(layerImage);
m_Groups.push_back("");
m_GroupToLabelMap.push_back({});
for (auto label : labels)
{
if (m_LabelMap.end() != m_LabelMap.find(label->GetValue()))
{
mitkThrow() << "Cannot add layer. Labels that should be added with layer use at least one label value that is already in use. Conflicted label value: " << label->GetValue();
}
auto labelClone = label->Clone();
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(labelClone);
this->AddLabelToMap(labelClone->GetValue(), labelClone, newGroupID);
this->RegisterLabel(labelClone);
}
this->Modified();
this->InvokeEvent(GroupAddedEvent(newGroupID));
return newGroupID;
}
void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& labelSet)
{
if (m_LayerContainer.size() <= groupID)
{
mitkThrow() << "Trying to replace labels of non-existing group. Invalid group id: "<<groupID;
}
//remove old group labels
LabelValueVectorType oldLabels;
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
oldLabels = this->m_GroupToLabelMap[groupID];
for (auto labelID : oldLabels)
{
this->RemoveLabelFromMap(labelID);
this->InvokeEvent(LabelRemovedEvent(labelID));
}
}
this->InvokeEvent(LabelsChangedEvent(oldLabels));
this->InvokeEvent(GroupModifiedEvent(groupID));
//add new labels to group
for (auto label : labelSet)
{
this->AddLabel(label->Clone(), groupID, true, false);
}
}
void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& labelSet)
{
return ReplaceGroupLabels(groupID, ConvertLabelVectorConst(labelSet));
}
mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID)
{
if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
return groupID == this->GetActiveLayer() ? this : m_LayerContainer[groupID];
}
const mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
return groupID == this->GetActiveLayer() ? this : m_LayerContainer.at(groupID).GetPointer();
}
const mitk::Image* mitk::LabelSetImage::GetGroupImageWorkaround(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
if (groupID == this->GetActiveLayer() && this->GetMTime()> m_LayerContainer[groupID]->GetMTime())
{ //we have to transfer the content first into the group image
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (groupID));
}
else
{
AccessByItk_1(this, ImageToLayerContainerProcessing, groupID);
}
}
return m_LayerContainer[groupID].GetPointer();
}
const std::string& mitk::LabelSetImage::GetGroupName(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot return group name. Group ID is invalid. Invalid ID: " << groupID;
return m_Groups[groupID];
}
void mitk::LabelSetImage::SetGroupName(GroupIndexType groupID, const std::string& name)
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot set group name. Group ID is invalid. Invalid ID: " << groupID;
m_Groups[groupID] = name;
this->InvokeEvent(GroupModifiedEvent(groupID));
}
void mitk::LabelSetImage::SetActiveLayer(unsigned int layer)
{
try
{
if (4 == this->GetDimension())
{
if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
{
BeforeChangeLayerEvent.Send();
if (m_activeLayerInvalid)
{
// We should not write the invalid layer back to the vector
m_activeLayerInvalid = false;
}
else
{
AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (GetActiveLayer()));
}
m_ActiveLayer = layer;
AccessFixedDimensionByItk_n(this, LayerContainerToImageProcessing, 4, (GetActiveLayer()));
AfterChangeLayerEvent.Send();
}
}
else
{
if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
{
BeforeChangeLayerEvent.Send();
if (m_activeLayerInvalid)
{
// We should not write the invalid layer back to the vector
m_activeLayerInvalid = false;
}
else
{
AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer());
}
m_ActiveLayer = layer;
AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer());
AfterChangeLayerEvent.Send();
}
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->Modified();
}
void mitk::LabelSetImage::SetActiveLabel(LabelValueType label)
{
m_ActiveLabelValue = label;
if (label != UNLABELED_VALUE)
{
auto groupID = this->GetGroupIndexOfLabel(label);
if (groupID!=this->GetActiveLayer()) this->SetActiveLayer(groupID);
}
Modified();
}
void mitk::LabelSetImage::ClearBuffer()
{
try
{
ClearImageBuffer(this);
this->Modified();
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
}
void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, PixelType sourcePixelValue)
{
try
{
AccessByItk_2(this, MergeLabelProcessing, pixelValue, sourcePixelValue);
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->SetActiveLabel(pixelValue);
this->InvokeEvent(LabelModifiedEvent(sourcePixelValue));
this->InvokeEvent(LabelModifiedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ sourcePixelValue, pixelValue }));
Modified();
}
void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, const std::vector<PixelType>& vectorOfSourcePixelValues)
{
try
{
for (unsigned int idx = 0; idx < vectorOfSourcePixelValues.size(); idx++)
{
AccessByItk_2(this, MergeLabelProcessing, pixelValue, vectorOfSourcePixelValues[idx]);
this->InvokeEvent(LabelModifiedEvent(vectorOfSourcePixelValues[idx]));
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->SetActiveLabel(pixelValue);
this->InvokeEvent(LabelModifiedEvent(pixelValue));
auto modifiedValues = vectorOfSourcePixelValues;
modifiedValues.push_back(pixelValue);
this->InvokeEvent(LabelsChangedEvent(modifiedValues));
Modified();
}
void mitk::LabelSetImage::RemoveLabel(LabelValueType pixelValue)
{
GroupIndexType groupID = 0;
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) return;
groupID = this->GetGroupIndexOfLabel(pixelValue);
//first erase the pixel content (also triggers a LabelModified event)
this->EraseLabel(pixelValue);
this->RemoveLabelFromMap(pixelValue);
if (m_ActiveLabelValue == pixelValue)
{
this->SetActiveLabel(0);
}
}
this->InvokeEvent(LabelRemovedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
this->InvokeEvent(GroupModifiedEvent(groupID));
}
void mitk::LabelSetImage::RemoveLabelFromMap(LabelValueType pixelValue)
{
if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) mitkThrow()<<"Invalid state of instance. RemoveLabelFromMap was called for unknown label id. invalid label id: "<<pixelValue;
auto groupID = this->GetGroupIndexOfLabel(pixelValue);
this->ReleaseLabel(m_LabelMap[pixelValue]);
//now remove the label entry itself
m_LabelMap.erase(pixelValue);
m_LabelToGroupMap.erase(pixelValue);
auto labelsInGroup = m_GroupToLabelMap[groupID];
labelsInGroup.erase(std::remove(labelsInGroup.begin(), labelsInGroup.end(), pixelValue), labelsInGroup.end());
m_GroupToLabelMap[groupID] = labelsInGroup;
}
void mitk::LabelSetImage::RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues)
{
for (const auto labelValue : vectorOfLabelPixelValues)
{
this->RemoveLabel(labelValue);
}
this->InvokeEvent(LabelsChangedEvent(vectorOfLabelPixelValues));
}
void mitk::LabelSetImage::EraseLabel(LabelValueType pixelValue)
{
try
{
auto groupID = this->GetGroupIndexOfLabel(pixelValue);
mitk::Image* groupImage = this->GetGroupImage(groupID);
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_1(groupImage, EraseLabelProcessing, 4, pixelValue);
}
else
{
AccessByItk_1(groupImage, EraseLabelProcessing, pixelValue);
}
groupImage->Modified();
}
catch (const itk::ExceptionObject& e)
{
mitkThrow() << e.GetDescription();
}
this->InvokeEvent(LabelModifiedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
Modified();
}
void mitk::LabelSetImage::EraseLabels(const LabelValueVectorType& labelValues)
{
for (auto labelValue : labelValues)
{
this->EraseLabel(labelValue);
}
}
mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::GetUnusedLabelValue() const
{
auto usedValues = this->GetUsedLabelValues();
return usedValues.back() + 1;
}
mitk::Label* mitk::LabelSetImage::AddLabel(mitk::Label* label, GroupIndexType groupID, bool addAsClone, bool correctLabelValue)
{
if (nullptr == label) mitkThrow() << "Invalid use of AddLabel. label is not valid.";
mitk::Label::Pointer newLabel = label;
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1;
if (m_LayerContainer.size() >= max_size)
return nullptr;
if (addAsClone) newLabel = label->Clone();
auto pixelValue = newLabel->GetValue();
auto usedValues = this->GetUsedLabelValues();
auto finding = std::find(usedValues.begin(), usedValues.end(), pixelValue);
if (!usedValues.empty() && usedValues.end() != finding)
{
if (correctLabelValue)
{
pixelValue = this->GetUnusedLabelValue();
newLabel->SetValue(pixelValue);
}
else
{
mitkThrow() << "Cannot add label due to conflicting label value that already exists in the MultiLabelSegmentation. Conflicting label value: " << pixelValue;
}
}
// add DICOM information of the label
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel);
this->AddLabelToMap(pixelValue, newLabel, groupID);
this->RegisterLabel(newLabel);
}
this->InvokeEvent(LabelAddedEvent(newLabel->GetValue()));
m_ActiveLabelValue = newLabel->GetValue();
this->Modified();
return newLabel;
}
mitk::Label* mitk::LabelSetImage::AddLabelWithContent(Label* label, const Image* labelContent, GroupIndexType groupID, LabelValueType contentLabelValue, bool addAsClone, bool correctLabelValue)
{
if (nullptr == labelContent) mitkThrow() << "Invalid use of AddLabel. labelContent is not valid.";
if (!Equal(*(this->GetTimeGeometry()), *(labelContent->GetTimeGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
mitkThrow() << "Invalid use of AddLabel. labelContent has not the same geometry like the segmentation.";
auto newLabel = this->AddLabel(label, groupID, addAsClone, correctLabelValue);
mitk::TransferLabelContent(labelContent, this->GetGroupImage(groupID), this->GetConstLabelsByValue(this->GetLabelValuesByGroup(groupID)),
mitk::LabelSetImage::UNLABELED_VALUE, mitk::LabelSetImage::UNLABELED_VALUE, false, { {contentLabelValue, newLabel->GetValue()}},
mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
this->Modified();
return newLabel;
}
mitk::Label* mitk::LabelSetImage::AddLabel(const std::string& name, const mitk::Color& color, GroupIndexType groupID)
{
mitk::Label::Pointer newLabel = mitk::Label::New();
newLabel->SetName(name);
newLabel->SetColor(color);
return AddLabel(newLabel,groupID,false);
}
void mitk::LabelSetImage::RenameLabel(LabelValueType pixelValue, const std::string& name, const mitk::Color& color)
{
std::shared_lock<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
auto label = GetLabel(pixelValue);
if (label.IsNull()) mitkThrow() << "Cannot rename label. Unknown label value provided. Unknown label value:" << pixelValue;
label->SetName(name);
label->SetColor(color);
this->UpdateLookupTable(pixelValue);
m_LookupTable->Modified();
// change DICOM information of the label
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
}
mitk::Label *mitk::LabelSetImage::GetActiveLabel()
{
if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
auto finding = m_LabelMap.find(m_ActiveLabelValue);
return finding == m_LabelMap.end() ? nullptr : finding->second;
}
const mitk::Label* mitk::LabelSetImage::GetActiveLabel() const
{
if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
auto finding = m_LabelMap.find(m_ActiveLabelValue);
return finding == m_LabelMap.end() ? nullptr : finding->second;
}
void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue)
{
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, 4, pixelValue);
}
else
{
AccessByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, pixelValue);
}
}
void mitk::LabelSetImage::SetLookupTable(mitk::LookupTable* lut)
{
m_LookupTable = lut;
this->Modified();
}
void mitk::LabelSetImage::UpdateLookupTable(PixelType pixelValue)
{
auto label = this->GetLabel(pixelValue);
if (label.IsNull()) mitkThrow() << "Cannot update lookup table. Unknown label value provided. Unknown label value:" << pixelValue;
const mitk::Color& color = label->GetColor();
double rgba[4];
m_LookupTable->GetTableValue(static_cast<int>(pixelValue), rgba);
rgba[0] = color.GetRed();
rgba[1] = color.GetGreen();
rgba[2] = color.GetBlue();
if (label->GetVisible())
rgba[3] = label->GetOpacity();
else
rgba[3] = 0.0;
m_LookupTable->SetTableValue(static_cast<int>(pixelValue), rgba);
}
unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const
{
if (layer >= m_Groups.size()) mitkThrow() << "Cannot get number of labels in group. Group is unknown. Invalid index:" << layer;
return m_GroupToLabelMap[layer].size();
}
unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const
{
return m_LabelMap.size();
}
void mitk::LabelSetImage::MaskStamp(mitk::Image *mask, bool forceOverwrite)
{
try
{
mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New();
padImageFilter->SetInput(0, mask);
padImageFilter->SetInput(1, this);
padImageFilter->SetPadConstant(0);
padImageFilter->SetBinaryFilter(false);
padImageFilter->SetLowerThreshold(0);
padImageFilter->SetUpperThreshold(1);
padImageFilter->Update();
mitk::Image::Pointer paddedMask = padImageFilter->GetOutput();
if (paddedMask.IsNull())
return;
AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite);
}
catch (...)
{
mitkThrow() << "Could not stamp the provided mask on the selected label.";
}
}
void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image)
{
if (image.IsNull() || image->IsEmpty() || !image->IsInitialized())
mitkThrow() << "Invalid labeled image.";
try
{
this->Initialize(image);
unsigned int byteSize = sizeof(LabelSetImage::PixelType);
for (unsigned int dim = 0; dim < image->GetDimension(); ++dim)
{
byteSize *= image->GetDimension(dim);
}
mitk::ImageWriteAccessor *accessor = new mitk::ImageWriteAccessor(static_cast<mitk::Image *>(this));
memset(accessor->GetData(), 0, byteSize);
delete accessor;
auto geometry = image->GetTimeGeometry()->Clone();
this->SetTimeGeometry(geometry);
if (image->GetDimension() == 3)
{
AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3);
}
else if (image->GetDimension() == 4)
{
AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 4);
}
else
{
mitkThrow() << image->GetDimension() << "-dimensional label set images not yet supported";
}
}
catch (Exception& e)
{
mitkReThrow(e) << "Could not initialize by provided labeled image.";
}
catch (...)
{
mitkThrow() << "Could not initialize by provided labeled image due to unknown error.";
}
this->Modified();
}
template <typename LabelSetImageType, typename ImageType>
void mitk::LabelSetImage::InitializeByLabeledImageProcessing(LabelSetImageType *labelSetImage, ImageType *image)
{
typedef itk::ImageRegionConstIteratorWithIndex<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<LabelSetImageType> TargetIteratorType;
TargetIteratorType targetIter(labelSetImage, labelSetImage->GetRequestedRegion());
targetIter.GoToBegin();
SourceIteratorType sourceIter(image, image->GetRequestedRegion());
sourceIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
const auto originalSourceValue = sourceIter.Get();
const auto sourceValue = static_cast<PixelType>(originalSourceValue);
if (originalSourceValue > mitk::Label::MAX_LABEL_VALUE)
{
mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains a pixel value that exceeds the label value range. Invalid pixel value:" << originalSourceValue;
}
targetIter.Set(sourceValue);
if (LabelSetImage::UNLABELED_VALUE!=sourceValue && !this->ExistLabel(sourceValue))
{
if (this->GetTotalNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE)
{
mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains to many labels.";
}
std::stringstream name;
name << "object-" << sourceValue;
double rgba[4];
this->GetLookupTable()->GetTableValue(sourceValue, rgba);
mitk::Color color;
color.SetRed(rgba[0]);
color.SetGreen(rgba[1]);
color.SetBlue(rgba[2]);
auto label = mitk::Label::New();
label->SetName(name.str().c_str());
label->SetColor(color);
label->SetOpacity(rgba[3]);
label->SetValue(sourceValue);
this->AddLabel(label,0,false);
}
++sourceIter;
++targetIter;
}
}
template <typename ImageType>
void mitk::LabelSetImage::MaskStampProcessing(ImageType *itkImage, mitk::Image *mask, bool forceOverwrite)
{
typename ImageType::Pointer itkMask;
mitk::CastToItkImage(mask, itkMask);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(itkMask, itkMask->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion());
targetIter.GoToBegin();
const auto activeLabel = this->GetActiveLabel()->GetValue();
while (!sourceIter.IsAtEnd())
{
PixelType sourceValue = sourceIter.Get();
PixelType targetValue = targetIter.Get();
if ((sourceValue != UNLABELED_VALUE) &&
(forceOverwrite || !this->IsLabelLocked(targetValue))) // skip unlabeled pixels and locked labels
{
targetIter.Set(activeLabel);
}
++sourceIter;
++targetIter;
}
this->Modified();
}
template <typename ImageType>
void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType *itkImage, LabelValueType pixelValue)
{
if (ImageType::GetImageDimension() != 3)
{
return;
}
auto labelGeometryFilter = itk::LabelGeometryImageFilter<ImageType>::New();
labelGeometryFilter->SetInput(itkImage);
labelGeometryFilter->Update();
auto centroid = labelGeometryFilter->GetCentroid(pixelValue);
mitk::Point3D pos;
pos[0] = centroid[0];
pos[1] = centroid[1];
pos[2] = centroid[2];
auto label = this->GetLabel(pixelValue);
if (label.IsNotNull())
{
label->SetCenterOfMassIndex(pos);
this->GetSlicedGeometry()->IndexToWorld(pos, pos);
label->SetCenterOfMassCoordinates(pos);
}
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::LabelSetImage::LayerContainerToImageProcessing(itk::Image<TPixel, VImageDimension> *target,
unsigned int layer)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typename ImageType::Pointer itkSource;
// mitk::CastToItkImage(m_LayerContainer[layer], itkSource);
itkSource = ImageToItkImage<TPixel, VImageDimension>(m_LayerContainer[layer]);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(target, target->GetLargestPossibleRegion());
targetIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::LabelSetImage::ImageToLayerContainerProcessing(const itk::Image<TPixel, VImageDimension> *source,
unsigned int layer) const
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typename ImageType::Pointer itkTarget;
// mitk::CastToItkImage(m_LayerContainer[layer], itkTarget);
itkTarget = ImageToItkImage<TPixel, VImageDimension>(m_LayerContainer[layer]);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(source, source->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion());
targetIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
m_LayerContainer[layer]->Modified();
}
template <typename ImageType>
void mitk::LabelSetImage::EraseLabelProcessing(ImageType *itkImage, PixelType pixelValue)
{
typedef itk::ImageRegionIterator<ImageType> IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
PixelType value = iter.Get();
if (value == pixelValue)
{
iter.Set(0);
}
++iter;
}
}
template <typename ImageType>
void mitk::LabelSetImage::MergeLabelProcessing(ImageType *itkImage, PixelType pixelValue, PixelType index)
{
typedef itk::ImageRegionIterator<ImageType> IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
if (iter.Get() == index)
{
iter.Set(pixelValue);
}
++iter;
}
}
void mitk::LabelSetImage::AddLabelToMap(LabelValueType labelValue, mitk::Label* label, GroupIndexType groupID)
{
if (m_LabelMap.find(labelValue)!=m_LabelMap.end())
mitkThrow() << "Segmentation is in an invalid state: Label value collision. A label was added with a LabelValue already in use. LabelValue: " << labelValue;
if (!this->ExistGroup(groupID))
mitkThrow() << "Cannot add label. Defined group is unknown. Invalid group index: " << groupID;
m_LabelMap[labelValue] = label;
m_LabelToGroupMap[labelValue] = groupID;
auto groupFinding = std::find(m_GroupToLabelMap[groupID].begin(), m_GroupToLabelMap[groupID].end(), labelValue);
if (groupFinding == m_GroupToLabelMap[groupID].end())
{
m_GroupToLabelMap[groupID].push_back(labelValue);
}
}
void mitk::LabelSetImage::RegisterLabel(mitk::Label* label)
{
if (nullptr == label) mitkThrow() << "Invalid call of RegisterLabel with a nullptr.";
UpdateLookupTable(label->GetValue());
m_LookupTable->Modified();
auto command = itk::MemberCommand<LabelSetImage>::New();
command->SetCallbackFunction(this, &LabelSetImage::OnLabelModified);
m_LabelModEventGuardMap.emplace(label->GetValue(), ITKEventObserverGuard(label, itk::ModifiedEvent(), command));
}
void mitk::LabelSetImage::ReleaseLabel(Label* label)
{
if (nullptr == label) mitkThrow() << "Invalid call of ReleaseLabel with a nullptr.";
m_LabelModEventGuardMap.erase(label->GetValue());
}
void mitk::LabelSetImage::ApplyToLabels(const LabelValueVectorType& values, std::function<void(Label*)>&& lambda)
{
auto labels = this->GetLabelsByValue(values);
std::for_each(labels.begin(), labels.end(), lambda);
this->InvokeEvent(LabelsChangedEvent(values));
}
void mitk::LabelSetImage::VisitLabels(const LabelValueVectorType& values, std::function<void(const Label*)>&& lambda) const
{
auto labels = this->GetConstLabelsByValue(values);
std::for_each(labels.begin(), labels.end(), lambda);
}
void mitk::LabelSetImage::OnLabelModified(const Object* sender, const itk::EventObject&)
{
auto label = dynamic_cast<const Label*>(sender);
if (nullptr == label)
mitkThrow() << "LabelSet is in wrong state. LabelModified event is not send by a label instance.";
Superclass::Modified();
this->InvokeEvent(LabelModifiedEvent(label->GetValue()));
}
bool mitk::LabelSetImage::ExistLabel(LabelValueType value) const
{
auto finding = m_LabelMap.find(value);
return m_LabelMap.end() != finding;
}
bool mitk::LabelSetImage::ExistLabel(LabelValueType value, GroupIndexType groupIndex) const
{
auto finding = m_LabelToGroupMap.find(value);
if (m_LabelToGroupMap.end() != finding)
{
return finding->second == groupIndex;
}
return false;
}
bool mitk::LabelSetImage::ExistGroup(GroupIndexType index) const
{
return index < m_LayerContainer.size();
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::GetGroupIndexOfLabel(LabelValueType value) const
{
auto finding = m_LabelToGroupMap.find(value);
if (m_LabelToGroupMap.end() == finding)
{
mitkThrow()<< "Cannot deduce group index. Passed label value does not exist. Value: "<< value;
}
return finding->second;
}
mitk::Label::ConstPointer mitk::LabelSetImage::GetLabel(LabelValueType value) const
{
auto finding = m_LabelMap.find(value);
if (m_LabelMap.end() != finding)
{
return finding->second;
}
return nullptr;
};
mitk::Label::Pointer mitk::LabelSetImage::GetLabel(LabelValueType value)
{
auto finding = m_LabelMap.find(value);
if (m_LabelMap.end() != finding)
{
return finding->second;
}
return nullptr;
};
bool mitk::LabelSetImage::IsLabelLocked(LabelValueType value) const
{
if (value == UNLABELED_VALUE)
{
return m_UnlabeledLabelLock;
}
const auto label = this->GetLabel(value);
return label->GetLocked();
}
const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetLabels() const
{
ConstLabelVectorType result;
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(label);
}
return result;
}
const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabels()
{
LabelVectorType result;
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(label);
}
return result;
}
const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing)
{
LabelVectorType result;
for (const auto& labelValue : labelValues)
{
Label::Pointer label = this->GetLabel(labelValue);
if (label.IsNotNull())
{
result.emplace_back(label);
}
else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
}
return result;
}
const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing) const
{
ConstLabelVectorType result;
for (const auto& labelValue : labelValues)
{
Label::ConstPointer label = this->GetLabel(labelValue);
if (label.IsNotNull())
{
result.emplace_back(label);
}
else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
}
return result;
}
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByGroup(GroupIndexType index) const
{
if (!this->ExistGroup(index))
mitkThrow() << "Cannot get labels of an invalid group. Invalid group index: " << index;
return m_GroupToLabelMap[index];
}
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByName(GroupIndexType index, const std::string_view name) const
{
LabelValueVectorType result;
auto searchName = [&result, name](const Label* l) { if(l->GetName() == name) result.push_back(l->GetValue()); };
this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
return result;
}
std::vector<std::string> mitk::LabelSetImage::GetLabelClassNames() const
{
std::set<std::string> names;
auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
this->VisitLabels(this->GetAllLabelValues(), searchName);
return std::vector<std::string>(names.begin(), names.end());
}
std::vector<std::string> mitk::LabelSetImage::GetLabelClassNamesByGroup(GroupIndexType index) const
{
std::set<std::string> names;
auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
return std::vector<std::string>(names.begin(), names.end());
}
void mitk::LabelSetImage::SetAllLabelsVisible(bool visible)
{
auto setVisibility = [visible,this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetAllLabelValues(), setVisibility);
this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible)
{
auto setVisibility = [visible, this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetLabelValuesByGroup(group), setVisibility);
this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsVisibleByName(GroupIndexType group, const std::string_view name, bool visible)
{
auto setVisibility = [visible, this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetLabelValuesByName(group, name), setVisibility);
this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsLocked(bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetAllLabelValues(), setLock);
}
void mitk::LabelSetImage::SetAllLabelsLockedByGroup(GroupIndexType group, bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetLabelValuesByGroup(group), setLock);
}
void mitk::LabelSetImage::SetAllLabelsLockedByName(GroupIndexType group, const std::string_view name, bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetLabelValuesByName(group, name), setLock);
}
bool mitk::Equal(const mitk::LabelSetImage &leftHandSide,
const mitk::LabelSetImage &rightHandSide,
ScalarType eps,
bool verbose)
{
bool returnValue = true;
/* LabelSetImage members */
MITK_INFO(verbose) << "--- LabelSetImage Equal ---";
// m_LookupTable;
const mitk::LookupTable* lhsLUT = leftHandSide.GetLookupTable();
const mitk::LookupTable* rhsLUT = rightHandSide.GetLookupTable();
returnValue = *lhsLUT == *rhsLUT;
if (!returnValue)
{
MITK_INFO(verbose) << "Lookup tables not equal.";
return returnValue;
;
}
// number layers
returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers();
if (!returnValue)
{
MITK_INFO(verbose) << "Number of layers not equal.";
return false;
}
// total number labels
returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels();
if (!returnValue)
{
MITK_INFO(verbose) << "Total number of labels not equal.";
return false;
}
// active layer
returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer();
if (!returnValue)
{
MITK_INFO(verbose) << "Active layer not equal.";
return false;
}
if (4 == leftHandSide.GetDimension())
{
MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
}
else
{
// working image data
returnValue = mitk::Equal((const mitk::Image &)leftHandSide, (const mitk::Image &)rightHandSide, eps, verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Working image data not equal.";
return false;
}
}
if (leftHandSide.GetTotalNumberOfLabels() != rightHandSide.GetTotalNumberOfLabels())
{
MITK_INFO(verbose) << "Number of labels are not equal.";
return false;
}
for (unsigned int layerIndex = 0; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++)
{
if (4 == leftHandSide.GetDimension())
{
MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
}
else
{
// layer image data
returnValue =
mitk::Equal(*leftHandSide.GetGroupImage(layerIndex), *rightHandSide.GetGroupImage(layerIndex), eps, verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Layer image data not equal.";
return false;
}
}
// label data
auto leftLabelsInGroup = leftHandSide.GetLabelValuesByGroup(layerIndex);
auto rightLabelsInGroup = rightHandSide.GetLabelValuesByGroup(layerIndex);
if (leftLabelsInGroup.size()!=rightLabelsInGroup.size())
{
MITK_INFO(verbose) << "Number of layer labels is not equal. Invalid layer:" <<layerIndex;
return false;
}
for (ConstLabelVector::size_type index = 0; index < leftLabelsInGroup.size(); ++index)
{
if (!mitk::Equal(*(leftHandSide.GetLabel(leftLabelsInGroup[index])), *(rightHandSide.GetLabel(rightLabelsInGroup[index])),eps,verbose))
{
MITK_INFO(verbose) << "At least one label in layer is not equal. Invalid layer:" << layerIndex;
return false;
}
}
}
return returnValue;
}
bool mitk::Equal(const mitk::LabelSetImage::ConstLabelVectorType& leftHandSide,
const mitk::LabelSetImage::ConstLabelVectorType& rightHandSide, ScalarType eps, bool verbose)
{
bool returnValue = true;
// container size;
returnValue = leftHandSide.size() == rightHandSide.size();
if (!returnValue)
{
MITK_INFO(verbose) << "Number of labels not equal.";
return returnValue;
;
}
// m_LabelContainer;
auto lhsit = leftHandSide.begin();
auto rhsit = rightHandSide.begin();
for (; lhsit != leftHandSide.end(); ++lhsit, ++rhsit)
{
returnValue = mitk::Equal(**rhsit, **lhsit,eps,verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Label in label container not equal.";
return returnValue;
;
}
}
return returnValue;
}
/**Helper function to convert a vector of labels into a label map
* @pre every label in the vector has a unique value.*/
using ConstLabelMapType = std::map<mitk::LabelSetImage::LabelValueType, mitk::Label::ConstPointer>;
ConstLabelMapType ConvertLabelVectorToMap(const mitk::ConstLabelVector& labelV)
{
ConstLabelMapType result;
for (auto label : labelV)
{
const auto value = label->GetValue();
auto finding = result.find(value);
if (finding != result.end()) mitkThrow() << "Operation failed. Cannot convert label vector into label map, because at least one label value is not unique. Violating label value: " << value;
result.insert(std::make_pair(value, label));
}
return result;
}
/** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter.
* For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter.
*/
template <class TDestinationPixel, class TSourcePixel, class TOutputpixel>
class LabelTransferFunctor
{
public:
LabelTransferFunctor() {};
LabelTransferFunctor(const ConstLabelMapType& destinationLabels, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked,
mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle,
mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) :
m_DestinationLabels(destinationLabels), m_SourceBackground(sourceBackground),
m_DestinationBackground(destinationBackground), m_DestinationBackgroundLocked(destinationBackgroundLocked),
m_SourceLabel(sourceLabel), m_NewDestinationLabel(newDestinationLabel), m_MergeStyle(mergeStyle), m_OverwriteStyle(overwriteStyle)
{
};
~LabelTransferFunctor() {};
bool operator!=(const LabelTransferFunctor& other)const
{
return !(*this == other);
}
bool operator==(const LabelTransferFunctor& other) const
{
return this->m_SourceBackground == other.m_SourceBackground &&
this->m_DestinationBackground == other.m_DestinationBackground &&
this->m_DestinationBackgroundLocked == other.m_DestinationBackgroundLocked &&
this->m_SourceLabel == other.m_SourceLabel &&
this->m_NewDestinationLabel == other.m_NewDestinationLabel &&
this->m_MergeStyle == other.m_MergeStyle &&
this->m_OverwriteStyle == other.m_OverwriteStyle &&
this->m_DestinationLabels == other.m_DestinationLabels;
}
LabelTransferFunctor& operator=(const LabelTransferFunctor& other)
{
this->m_DestinationLabels = other.m_DestinationLabels;
this->m_SourceBackground = other.m_SourceBackground;
this->m_DestinationBackground = other.m_DestinationBackground;
this->m_DestinationBackgroundLocked = other.m_DestinationBackgroundLocked;
this->m_SourceLabel = other.m_SourceLabel;
this->m_NewDestinationLabel = other.m_NewDestinationLabel;
this->m_MergeStyle = other.m_MergeStyle;
this->m_OverwriteStyle = other.m_OverwriteStyle;
return *this;
}
inline TOutputpixel operator()(const TDestinationPixel& existingDestinationValue, const TSourcePixel& existingSourceValue)
{
if (existingSourceValue == this->m_SourceLabel)
{
if (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle)
{
return this->m_NewDestinationLabel;
}
else
{
if (existingDestinationValue == m_DestinationBackground)
{
if (!m_DestinationBackgroundLocked)
{
return this->m_NewDestinationLabel;
}
}
else
{
auto labelFinding = this->m_DestinationLabels.find(existingDestinationValue);
if (labelFinding==this->m_DestinationLabels.end() || !labelFinding->second->GetLocked())
{
return this->m_NewDestinationLabel;
}
}
}
}
else if (mitk::MultiLabelSegmentation::MergeStyle::Replace == this->m_MergeStyle
&& existingSourceValue == this->m_SourceBackground
&& existingDestinationValue == this->m_NewDestinationLabel
&& (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle
|| !this->m_DestinationBackgroundLocked))
{
return this->m_DestinationBackground;
}
return existingDestinationValue;
}
private:
ConstLabelMapType m_DestinationLabels;
mitk::Label::PixelType m_SourceBackground = 0;
mitk::Label::PixelType m_DestinationBackground = 0;
bool m_DestinationBackgroundLocked = false;
mitk::Label::PixelType m_SourceLabel = 1;
mitk::Label::PixelType m_NewDestinationLabel = 1;
mitk::MultiLabelSegmentation::MergeStyle m_MergeStyle = mitk::MultiLabelSegmentation::MergeStyle::Replace;
mitk::MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks;
};
/**Helper function used by TransferLabelContentAtTimeStep to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/
template<unsigned int VImageDimension>
void TransferLabelContentAtTimeStepHelper(const itk::Image<mitk::Label::PixelType, VImageDimension>* itkSourceImage, mitk::Image* destinationImage,
const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground,
bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle, mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle)
{
typedef itk::Image<mitk::Label::PixelType, VImageDimension> ContentImageType;
typename ContentImageType::Pointer itkDestinationImage;
mitk::CastToItkImage(destinationImage, itkDestinationImage);
auto sourceRegion = itkSourceImage->GetLargestPossibleRegion();
auto relevantRegion = itkDestinationImage->GetLargestPossibleRegion();
bool overlapping = relevantRegion.Crop(sourceRegion);
if (!overlapping)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage and destinationImage seem to have no overlapping image region.";
}
typedef LabelTransferFunctor <mitk::Label::PixelType, mitk::Label::PixelType, mitk::Label::PixelType> LabelTransferFunctorType;
typedef itk::BinaryFunctorImageFilter<ContentImageType, ContentImageType, ContentImageType, LabelTransferFunctorType> FilterType;
LabelTransferFunctorType transferFunctor(ConvertLabelVectorToMap(destinationLabels), sourceBackground, destinationBackground,
destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStyle);
auto transferFilter = FilterType::New();
transferFilter->SetFunctor(transferFunctor);
transferFilter->InPlaceOn();
transferFilter->SetInput1(itkDestinationImage);
transferFilter->SetInput2(itkSourceImage);
transferFilter->GetOutput()->SetRequestedRegion(relevantRegion);
transferFilter->Update();
}
void mitk::TransferLabelContentAtTimeStep(
const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, const TimeStepType timeStep, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage must not be null.";
}
if (sourceImage == destinationImage && labelMapping.size() > 1)
{
MITK_DEBUG << "Warning. Using TransferLabelContentAtTimeStep or TransferLabelContent with equal source and destination and more then on label to transfer, can lead to wrong results. Please see documentation and verify that the usage is OK.";
}
Image::ConstPointer sourceImageAtTimeStep = SelectImageByTimeStep(sourceImage, timeStep);
Image::Pointer destinationImageAtTimeStep = SelectImageByTimeStep(destinationImage, timeStep);
if (nullptr == sourceImageAtTimeStep)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage does not have the requested time step: " << timeStep;
}
if (nullptr == destinationImageAtTimeStep)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage does not have the requested time step: " << timeStep;
}
if (!Equal(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
{
if (IsSubGeometry(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true))
{
//we have to pad the source image
//because ImageToImageFilters always check for origin matching even if
//the requested output region is fitting :(
auto padFilter = mitk::PadImageFilter::New();
padFilter->SetInput(0, sourceImageAtTimeStep);
padFilter->SetInput(1, destinationImageAtTimeStep);
padFilter->SetPadConstant(Label::UNLABELED_VALUE);
padFilter->SetBinaryFilter(false);
padFilter->Update();
sourceImageAtTimeStep = padFilter->GetOutput();
}
else
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; source image has neither the same geometry than destination image nor has the source image a sub geometry.";
}
}
auto destLabelMap = ConvertLabelVectorToMap(destinationLabels);
for (const auto& [sourceLabel, newDestinationLabel] : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE!=newDestinationLabel && destLabelMap.end() == destLabelMap.find(newDestinationLabel))
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel;
}
AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentAtTimeStepHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabels, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye));
}
destinationImage->Modified();
}
void mitk::TransferLabelContent(
const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
}
const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
{
mitkThrow() << "Invalid call of TransferLabelContent; mismatch between images in number of time steps.";
}
for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
{
TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, i, sourceBackground,
destinationBackground, destinationBackgroundLocked, labelMapping, mergeStyle, overwriteStlye);
}
}
void mitk::TransferLabelContentAtTimeStep(
const LabelSetImage* sourceImage, LabelSetImage* destinationImage, const TimeStepType timeStep,
LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
}
auto destinationLabels = destinationImage->GetConstLabelsByValue(destinationImage->GetLabelValuesByGroup(destinationImage->GetActiveLayer()));
for (const auto& mappingElement : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE != mappingElement.first && !sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer()))
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first;
}
}
TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, timeStep, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, destinationImage->GetUnlabeledLabelLock(),
labelMapping, mergeStyle, overwriteStlye);
}
void mitk::TransferLabelContent(
const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
}
const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
{
mitkThrow() << "Invalid call of TransferLabelContent; images have no equal number of time steps.";
}
for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
{
TransferLabelContentAtTimeStep(sourceImage, destinationImage, i, labelMapping, mergeStyle, overwriteStlye);
}
}
diff --git a/Modules/Multilabel/mitkLabelSetImage.h b/Modules/Multilabel/mitkLabelSetImage.h
index 0356818184..997a859e44 100644
--- a/Modules/Multilabel/mitkLabelSetImage.h
+++ b/Modules/Multilabel/mitkLabelSetImage.h
@@ -1,711 +1,711 @@
/*============================================================================
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 mitkLabelSetImage_h
#define mitkLabelSetImage_h
#include <shared_mutex>
#include <mitkImage.h>
#include <mitkLabel.h>
#include <mitkLookupTable.h>
#include <mitkMultiLabelEvents.h>
#include <mitkMessage.h>
#include <mitkITKEventObserverGuard.h>
#include <MitkMultilabelExports.h>
namespace mitk
{
/** @brief LabelSetImage class for handling labels and layers in a segmentation session.
*
* Events that are potentially send by the class in regard to groups or labels:
* - LabelAddedEvent is emitted whenever a new label has been added.
* - LabelModifiedEvent is emitted whenever a label has been modified.
* - LabelRemovedEvent is emitted whenever a label has been removed.
* - LabelsChangedEvent is emitted when labels are changed (added, removed, modified). In difference to the other label events LabelsChanged is send only *one time* after the modification of the
* MultiLableImage instance is finished. So e.g. even if 4 labels are changed by a merge operation, this event will
* only be sent once (compared to LabelRemoved or LabelModified).
* - GroupAddedEvent is emitted whenever a new group has been added.
* - GroupModifiedEvent is emitted whenever a group has been modified.
* - GroupRemovedEvent is emitted whenever a label has been removed.
*
* @ingroup Data
*/
class MITKMULTILABEL_EXPORT LabelSetImage : public Image
{
public:
/**
* \brief BeforeChangeLayerEvent (e.g. used for GUI integration)
* As soon as active labelset should be changed, the signal emits.
* Emitted by SetActiveLayer(int layer);
*/
Message<> BeforeChangeLayerEvent;
/**
* \brief AfterchangeLayerEvent (e.g. used for GUI integration)
* As soon as active labelset was changed, the signal emits.
* Emitted by SetActiveLayer(int layer);
*/
Message<> AfterChangeLayerEvent;
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// FUTURE MultiLabelSegmentation:
// Section that already contains declarations used in the new class.
// So this part of the interface will stay after refactoring towards
// the new MultiLabelSegmentation class (see T28524). This section was introduced
// because some of the planned features are already urgently needed.
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
mitkClassMacro(LabelSetImage, Image);
itkNewMacro(Self);
typedef mitk::Label::PixelType PixelType;
using GroupIndexType = std::size_t;
using LabelValueType = mitk::Label::PixelType;
using ConstLabelVectorType = ConstLabelVector;
using LabelVectorType = LabelVector;
using LabelValueVectorType = std::vector<LabelValueType>;
const static LabelValueType UNLABELED_VALUE;
/** \brief Adds a label instance to a group of the multi label image.
* @remark By default, if the pixel value of the label is already used in the image, the label
* will get a new none conflicting value assigned. This can be controlled by correctLabelValue.
* @param label Instance of an label that should be added or used as template
* @param groupID The id of the group the label should be added to.
* @param addAsClone Flag that controls, if the passed instance should be added (false; the image will then take ownership,
* be aware that e.g. event observers will be added)
* a clone of the instance (true).
* @param correctLabelValue Flag that controls, if the value of the passed label should be correct, if this value is already used in
* the multi label image. True: Conflicting values will be corrected, be assigning a none conflicting value. False: If the value is conflicting
* an exception will be thrown.
* @return Instance of the label as it was added to the label set.
* @pre label must point to a valid instance.
* @pre If correctLabelValue==false, label value must be non conflicting.
* @pre groupID must indicate an existing group.
*/
mitk::Label* AddLabel(Label* label, GroupIndexType groupID, bool addAsClone = true, bool correctLabelValue = true);
/** \brief Adds a label instance to a group of the multi label image including its pixel content.
* @remark By default, if the pixel value of the label is already used in the image, the label
* will get a new none conflicting value assigned. This can be controlled by correctLabelValue.
* @param label Instance of a label that should be added or used as template
* @param groupID The id of the group the label should be added to.
* @param labelContent Pointer to an image that contains the pixel content of the label that should be added.
* @param contentLabelValue Pixel value in the content image that indicates the label (may not be the same like the label value
* used in the segmentation after addition).
* @param addAsClone Flag that controls, if the passed instance should be added (false; the image will then take ownership,
* be aware that e.g. event observers will be added)
* a clone of the instance (true).
* @param correctLabelValue Flag that controls, if the value of the passed label should be corrected, if this value is already used in
* the multi label image. True: Conflicting values will be corrected, by assigning a none conflicting value. False: If the value is conflicting
* an exception will be thrown.
* @return Instance of the label as it was added to the label set.
* @pre label must point to a valid instance.
* @pre If correctLabelValue==false, label value must be non conflicting.
* @pre groupID must indicate an existing group.
* @pre labelContent must point to a valid image that has the same geometry like the segmentation.
*/
mitk::Label* AddLabelWithContent(Label* label, const Image* labelContent, GroupIndexType groupID, LabelValueType contentLabelValue, bool addAsClone = true, bool correctLabelValue = true);
/** \brief Adds a new label to a group of the image by providing name and color.
* @param name (Class) name of the label instance that should be added.
* @param color Color of the new label instance.
* @param groupID The id of the group the label should be added to.
* @return Instance of the label as it was added to the label set.
* @pre groupID must indicate an existing group.
*/
mitk::Label* AddLabel(const std::string& name, const Color& color, GroupIndexType groupID);
/** \brief allows to adapt name and color of a certain label
* @param labelValue Value of the label that should be changed
* @param name New name for the label
* @param color New color for the label
* @pre Indicated label value must exist.
*/
void RenameLabel(LabelValueType labelValue, const std::string& name, const Color& color);
/**
* @brief Removes the label with the given value.
* The label is removed from the labelset and
* the pixel with the value of the label are set to UNLABELED_VALUE.
* @param labelValue the pixel value of the label to be removed. If the label is unknown,
* the method will return without doing anything.
*/
void RemoveLabel(LabelValueType labelValue);
/**
* @brief Removes labels from the mitk::MultiLabelSegmentation.
* The label is removed from the labelset and
* the pixel with the value of the label are set to UNLABELED_VALUE.
* If a label value does not exist, it will be ignored.
* @param vectorOfLabelPixelValues a list of labels to be removed
*/
void RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues);
/**
* @brief Erases the label with the given value from the labelset image.
* The label itself will not be erased from the respective mitk::LabelSet. In order to
* remove the label itself use mitk::LabelSetImage::RemoveLabels()
* @param labelValue the pixel value of the label that will be erased from the labelset image
* @pre labelValue must exist.
*/
void EraseLabel(LabelValueType labelValue);
/**
* @brief Erases a list of labels with the given values from the labelset image.
* @param labelValues the list of pixel values of the labels
* that will be erased from the labelset image
* @pre label values must exist
*/
void EraseLabels(const LabelValueVectorType& labelValues);
/**
* @brief Removes a whole group including all its labels.
* @remark with removing a group all groups with greater index will be re-indexed to
* close the gap. Hence externally stored spatial group indices may become invalid.
* @param group Group index of the spatial group that should be removed. If the spatial group does not exist, an
* exception will be raised.
* @pre group index must be valid.
*/
void RemoveGroup(GroupIndexType group);
/** \brief Returns true if the value exists in the MultiLabelSegmentation instance*/
bool ExistLabel(LabelValueType value) const;
/**
* @brief Checks if a label belongs in a certain spatial group
* @param value the label value
- * @param groupIndex Index of the spacial group which should be checked for the label
+ * @param groupIndex Index of the spatial group which should be checked for the label
* @return true if the label exists otherwise false
*/
bool ExistLabel(LabelValueType value, GroupIndexType groupIndex) const;
/**
* @brief Returns true if the spatial group exists in the MultiLabelSegmentation instance.
*
* @param index Group index of the group that should be checked for existence.
*/
bool ExistGroup(GroupIndexType index) const;
/** Returns the group id of the based label value.
* @pre label value must exists.
*/
GroupIndexType GetGroupIndexOfLabel(LabelValueType value) const;
/**
* @brief Returns the mitk::Label with the given value.
* @param value the pixel value of the label
* @return smart pointer to the label instance if defined in the segmentation, otherwise nullptr.
* @remark The label is returned as smart pointer, because the MultiLabelSegmentation instance
* gives no guarantee how long the label instance will be a valid label of the segmentation.
* If you hold the label instance for a longer time, you must expect that it is not valid anymore
* (Either because the label id was removed or the label instance was replaced). It is valid as long
* it points to the same label instance like a recent GetLabel() call.
*/
mitk::Label::ConstPointer GetLabel(LabelValueType value) const;
mitk::Label::Pointer GetLabel(LabelValueType value);
/** Returns a vector with pointers to all labels currently defined in the MultiLabelSegmentation
instance.*/
const ConstLabelVectorType GetLabels() const;
const LabelVectorType GetLabels();
/** Returns a vector of all label values currently defined in the MultiLabelSegmentation
instance.*/
const LabelValueVectorType GetAllLabelValues() const;
/** @brief Returns a vector with pointers to all labels in the MultiLabelSegmentation indicated
* by the passed label value vector.
* @param labelValues Vector of values of labels that should be returned.
* @param ignoreMissing If true (default), unknown labels Will be skipped in the result. If false,
* an exception will be raised, if a label is requested.
*/
const LabelVectorType GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = true);
/** @brief Returns a vector with const pointers to all labels in the MultiLabelSegmentation indicated
* by the passed label value vector.
* For details see GetLabelsByValue();
*/
const ConstLabelVectorType GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = false) const;
/** Helper function that can be used to extract a vector of label values of a vector of label instance pointers.*/
static LabelValueVectorType ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels);
/** Helper function that can be used to extract a vector of label values are vector of label instances.*/
static LabelValueVectorType ExtractLabelValuesFromLabelVector(const LabelVectorType& labels);
/** Helper function that converts a given vector of label instance pointers into a vector of const pointers.*/
static ConstLabelVectorType ConvertLabelVectorConst(const LabelVectorType& labels);
/**
* @brief Returns a vector of all label values located on the specified group.
* @param index the index of the group for which the vector of labels should be retrieved.
* If an invalid index is passed an exception will be raised.
* @return the respective vector of label values.
* @pre group index must exist.
*/
const LabelValueVectorType GetLabelValuesByGroup(GroupIndexType index) const;
/**
* @brief Returns a vector of all label values located on the specified group having a certain name.
* @param index the index of the group for which the vector of labels should be retrieved.
* If an invalid index is passed an exception will be raised.
* @param name Name of the label instances one is looking for.
* @return the respective vector of label values.
* @pre group index must exist.
*/
const LabelValueVectorType GetLabelValuesByName(GroupIndexType index, const std::string_view name) const;
/**
* Returns a vector with (class) names of all label instances used in the segmentation (over all groups)
*/
std::vector<std::string> GetLabelClassNames() const;
/**
* Returns a vector with (class) names of all label instances present in a certain group.
* @param index ID of the group, for which the label class names should be returned
* @pre Indicated group must exist. */
std::vector<std::string> GetLabelClassNamesByGroup(GroupIndexType index) const;
/** Helper that returns an unused label value, that could be used e.g. if one wants to define a label externally
* before adding it.
* @return A label value currently not in use.
* @remark is no unused label value can be provided an exception will be thrown.*/
LabelValueType GetUnusedLabelValue() const;
itkGetConstMacro(UnlabeledLabelLock, bool);
itkSetMacro(UnlabeledLabelLock, bool);
itkBooleanMacro(UnlabeledLabelLock);
/** Set the visibility of all label instances accordingly to the passed state.
*/
void SetAllLabelsVisible(bool visible);
/** Set the visibility of all label instances in a group accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible);
/** Set the visibility of all label instances In a group with a given class name
* accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsVisibleByName(GroupIndexType group, const std::string_view name, bool visible);
/** Returns the lock state of the label (including UnlabeledLabel value).
@pre Requested label does exist.*/
bool IsLabelLocked(LabelValueType value) const;
/** Set the lock state of all label instances accordingly to the passed state.
*/
void SetAllLabelsLocked(bool locked);
/** Set the lock state of all label instances in a group accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsLockedByGroup(GroupIndexType group, bool locked);
/** Set the lock state of all label instances In a group with a given class name
* accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsLockedByName(GroupIndexType group, const std::string_view name, bool locked);
/**
* \brief Replaces the labels of a group with a given vector of labels.
*
* @remark The passed label instances will be cloned before added to ensure clear ownership
* of the new labels.
* @remark The pixel content of the old labels will not be removed.
* @param groupID The index of the group that should have its labels replaced
* @param newLabels The vector of new labels
* @pre Group that should be replaced must exist.
* @pre new label values must not be used in other groups.
*/
void ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& newLabels);
void ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& newLabels);
/** Returns the pointer to the image that contains the labeling of the indicate group.
*@pre groupID must reference an existing group.*/
mitk::Image* GetGroupImage(GroupIndexType groupID);
/** Returns the pointer to the image that contains the labeling of the indicate group.
*@pre groupID must reference an existing group.*/
const mitk::Image* GetGroupImage(GroupIndexType groupID) const;
/** Returns the name of the indicated group. String may be empty if no name was defined.
* Remark: The name neither is guaranteed to be defined nor that it is unique. Use the index
* to uniquely refer to a group.
*@pre groupID must reference an existing group.*/
const std::string& GetGroupName(GroupIndexType groupID) const;
/** Set the name of a group.
*@pre groupID must reference an existing group.*/
void SetGroupName(GroupIndexType groupID, const std::string& name);
itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable);
void SetLookupTable(LookupTable* lut);
/** Updates the lookup table for a label indicated by the passed label value using the color of the label.
* @pre labelValue must exist.
*/
void UpdateLookupTable(PixelType pixelValue);
protected:
void OnLabelModified(const Object* sender, const itk::EventObject&);
/** Helper to ensure that the maps are correctly populated for a new label instance.*/
void AddLabelToMap(LabelValueType labelValue, Label* label, GroupIndexType groupID);
void RemoveLabelFromMap(LabelValueType labelValue);
/** Helper to ensure label events are correctly connected and lookup table is updated for a new label instance.*/
void RegisterLabel(Label* label);
/** Helper to ensure label events are unregistered.*/
void ReleaseLabel(Label* label);
/** Helper class used internally to apply lambda functions to the labels specified by the passed label value vector.
*/
void ApplyToLabels(const LabelValueVectorType& values, std::function<void(Label*)>&& lambda);
/** Helper class used internally to for visiting the labels specified by the passed label value vector
* with the lambda function.
*/
void VisitLabels(const LabelValueVectorType& values, std::function<void(const Label*)>&& lambda) const;
LabelValueType m_ActiveLabelValue;
private:
using LabelMapType = std::map<LabelValueType, Label::Pointer>;
/** Dictionary that holds all known labels (label value is the key).*/
LabelMapType m_LabelMap;
using GroupNameVectorType = std::vector<std::string>;
/** Vector storing the names of all groups. If a group has no user name defined, string is empty.*/
GroupNameVectorType m_Groups;
/**This type is internally used to track which label is currently
* associated with which layer.*/
using GroupToLabelMapType = std::vector<LabelValueVectorType>;
/* Dictionary that maps between group id (key) and label values in the group (vector of label value).*/
GroupToLabelMapType m_GroupToLabelMap;
using LabelToGroupMapType = std::map<LabelValueType, GroupIndexType>;
/* Dictionary that maps between label value (key) and group id (value)*/
LabelToGroupMapType m_LabelToGroupMap;
using LabelEventGuardMapType = std::map<LabelValueType, ITKEventObserverGuard>;
LabelEventGuardMapType m_LabelModEventGuardMap;
LookupTable::Pointer m_LookupTable;
/** Indicates if the MultiLabelSegmentation allows to overwrite unlabeled pixels in normal pixel manipulation operations (e.g. TransferLabelConent).*/
bool m_UnlabeledLabelLock;
/** Mutex used to secure manipulations of the internal state of label and group maps.*/
std::shared_mutex m_LabelNGroupMapsMutex;
public:
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// END FUTURE MultiLabelSegmentation
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
/** DON'T USE. WORKAROUND method that is used until the rework is finished to
ensure always getting a group image and not this.
@warning Don't use. This method is going to be removed as soon as T30194 is
solved.*/
const mitk::Image* GetGroupImageWorkaround(GroupIndexType groupID) const;
/**
* \brief */
void UpdateCenterOfMass(PixelType pixelValue);
/**
* @brief Initialize an empty mitk::LabelSetImage using the information
* of an mitk::Image
* @param image the image which is used for initializing the mitk::LabelSetImage
*/
using mitk::Image::Initialize;
void Initialize(const mitk::Image *image) override;
/**
* \brief removes all pixel content form the active layer.*/
void ClearBuffer();
/**
* @brief Merges the mitk::Label with a given target value with the active label
*
* @param pixelValue the value of the label that should be the new merged label
* @param sourcePixelValue the value of the label that should be merged into the specified one
*/
void MergeLabel(PixelType pixelValue, PixelType sourcePixelValue);
/**
* @brief Merges a list of mitk::Labels with the mitk::Label that has a specific value
*
* @param pixelValue the value of the label that should be the new merged label
* @param vectorOfSourcePixelValues the list of label values that should be merge into the specified one
*/
void MergeLabels(PixelType pixelValue, const std::vector<PixelType>& vectorOfSourcePixelValues);
/**
* @brief Gets the ID of the currently active layer
* @return the ID of the active layer
* @pre at least on group must exist.
*/
unsigned int GetActiveLayer() const;
Label* GetActiveLabel();
const Label* GetActiveLabel() const;
/**
* @brief Get the number of all existing mitk::Labels for a given layer
* @param layer the layer ID for which the active mitk::Labels should be retrieved
* @return the number of all existing mitk::Labels for the given layer
*/
unsigned int GetNumberOfLabels(unsigned int layer) const;
/**
* @brief Returns the number of all labels summed up across all layers
* @return the overall number of labels across all layers
*/
unsigned int GetTotalNumberOfLabels() const;
/** @brief Initialize a new mitk::LabelSetImage by a given image.
* For all distinct pixel values of the parameter image new labels will
* be created. If the number of distinct pixel values exceeds mitk::Label::MAX_LABEL_VALUE
* an exception will be raised.
* @param image the image which is used for initialization
*/
void InitializeByLabeledImage(mitk::Image::Pointer image);
void MaskStamp(mitk::Image *mask, bool forceOverwrite);
void SetActiveLayer(unsigned int layer);
void SetActiveLabel(LabelValueType label);
unsigned int GetNumberOfLayers() const;
/**
* \brief Adds a new layer to the LabelSetImage. The new layer will be set as the active one.
* \param labels Labels that will be added to the new layer if provided
* \return the layer ID of the new layer
*/
GroupIndexType AddLayer(ConstLabelVector labels = {});
/**
* \brief Adds a layer based on a provided mitk::Image.
* \param layerImage is added to the vector of label images
* \param labels labels that will be cloned and added to the new layer if provided
* \return the layer ID of the new layer
* \pre layerImage must be valid instance
* \pre layerImage needs to have the same geometry then the segmentation
* \pre layerImage must have the pixel value equal to LabelValueType.
*/
GroupIndexType AddLayer(mitk::Image* layerImage, ConstLabelVector labels = {});
protected:
mitkCloneMacro(Self);
LabelSetImage();
LabelSetImage(const LabelSetImage &other);
~LabelSetImage() override;
template <typename TPixel, unsigned int VImageDimension>
void LayerContainerToImageProcessing(itk::Image<TPixel, VImageDimension> *source, unsigned int layer);
template <typename TPixel, unsigned int VImageDimension>
void ImageToLayerContainerProcessing(const itk::Image<TPixel, VImageDimension> *source, unsigned int layer) const;
template <typename ImageType>
void CalculateCenterOfMassProcessing(ImageType *input, LabelValueType index);
template <typename ImageType>
void EraseLabelProcessing(ImageType *input, PixelType index);
template <typename ImageType>
void MergeLabelProcessing(ImageType *input, PixelType pixelValue, PixelType index);
template <typename ImageType>
void MaskStampProcessing(ImageType *input, mitk::Image *mask, bool forceOverwrite);
template <typename LabelSetImageType, typename ImageType>
void InitializeByLabeledImageProcessing(LabelSetImageType *input, ImageType *other);
/** helper needed for ensuring unique values.
returns a sorted list of all labels (including the value for Unlabeled pixels..*/
LabelValueVectorType GetUsedLabelValues() const;
std::vector<Image::Pointer> m_LayerContainer;
int m_ActiveLayer;
bool m_activeLayerInvalid;
};
/**
- * @brief Equal A function comparing two label set images for beeing equal in meta- and imagedata
+ * @brief Equal A function comparing two label set images for being equal in meta- and imagedata
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - LabelSetImage members
* - working image data
* - layer image data
* - labels in label set
*
* @param rightHandSide An image to be compared
* @param leftHandSide An image to be compared
* @param eps Tolerance 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
*/
MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSetImage &leftHandSide,
const mitk::LabelSetImage &rightHandSide,
ScalarType eps,
bool verbose);
/**
* @brief Equal A function comparing two vectors of labels for being equal in data
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - Labels in vector
*
* @param rightHandSide An vector of labels to be compared
* @param leftHandSide An vector of labels to be compared
* @param eps Tolerance 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
*/
MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSetImage::ConstLabelVectorType& leftHandSide,
const mitk::LabelSetImage::ConstLabelVectorType& rightHandSide,
ScalarType eps,
bool verbose);
/** temporary namespace that is used until the new class MultiLabelSegmentation is
introduced. It allows to already introduce/use some upcoming definitions, while
refactoring code.*/
namespace MultiLabelSegmentation
{
enum class MergeStyle
{
Replace, //The old label content of a label value will be replaced by its new label content.
//Therefore pixels that are labeled might become unlabeled again.
//(This means that a lock of the value is also ignored).
Merge //The union of old and new label content will be generated.
};
enum class OverwriteStyle
{
RegardLocks, //Locked labels in the same spatial group will not be overwritten/changed.
IgnoreLocks //Label locks in the same spatial group will be ignored, so these labels might be changed.
};
}
using LabelValueMappingVector = std::vector < std::pair<Label::PixelType, Label::PixelType> >;
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label for a specific time step. Function processes the whole image volume of the specified time step.
@remark in its current implementation the function only transfers contents of the active layer of the passed LabelSetImages.
@remark the function assumes that it is only called with source and destination image of same geometry.
@remark CAUTION: The function is not save if sourceImage and destinationImage are the same instance and more than one label is transferred,
because the changes are made in-place for performance reasons in multiple passes. If a mapped value A equals an "old value"
that occurs later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then later again, because
it is also an "old" value in the mapping table.
@param sourceImage Pointer to the LabelSetImage which active layer should be used as source for the transfer.
@param destinationImage Pointer to the LabelSetImage which active layer should be used as destination for the transfer.
@param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the
label in the source image. The second element is the label that transferred pixels should become in the destination image.
- The order in which the labels will be transfered is the same order of elements in the labelMapping.
+ The order in which the labels will be transferred is the same order of elements in the labelMapping.
If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transferring), keep in mind that
for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be
altered with MergeStyle Replace).
@param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of
MultiLabelSegmentation::MergeStyle.
@param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see
documentation of MultiLabelSegmentation::OverwriteStyle.
@param timeStep indicate the time step that should be transferred.
@pre sourceImage and destinationImage must be valid
@pre sourceImage and destinationImage must contain the indicated timeStep
@pre sourceImage must contain all indicated sourceLabels in its active layer.
@pre destinationImage must contain all indicated destinationLabels in its active layer.*/
MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
const TimeStepType timeStep, LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label. Function processes the whole image volume for all time steps.
For more details please see TransferLabelContentAtTimeStep for LabelSetImages.
@sa TransferLabelContentAtTimeStep*/
MITKMULTILABEL_EXPORT void TransferLabelContent(const LabelSetImage* sourceImage, LabelSetImage* destinationImage, LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label for a specific time step. Function processes the whole image volume of the specified time step.
@remark the function assumes that it is only called with source and destination image of same geometry.
@remark CAUTION: The function is not save, if sourceImage and destinationImage are the same instance and you transfer more then one
label, because the changes are made in-place for performance reasons but not in one pass. If a mapped value A equals a "old value"
that is later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then latter again, because
it is also an "old" value in the mapping table.
@param sourceImage Pointer to the image that should be used as source for the transfer.
@param destinationImage Pointer to the image that should be used as destination for the transfer.
@param destinationLabelVector Reference to the vector of labels (incl. lock states) in the destination image. Unknown pixel
values in the destinationImage will be assumed to be unlocked.
@param sourceBackground Value indicating the background in the source image.
@param destinationBackground Value indicating the background in the destination image.
@param destinationBackgroundLocked Value indicating the lock state of the background in the destination image.
@param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the
label in the source image. The second element is the label that transferred pixels should become in the destination image.
- The order in which the labels will be transfered is the same order of elements in the labelMapping.
+ The order in which the labels will be transferred is the same order of elements in the labelMapping.
If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transferring), keep in mind that
for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be
altered with MergeStyle Replace).
@param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of
MultiLabelSegmentation::MergeStyle.
@param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see
documentation of MultiLabelSegmentation::OverwriteStyle.
@param timeStep indicate the time step that should be transferred.
@pre sourceImage, destinationImage and destinationLabelVector must be valid
@pre sourceImage and destinationImage must contain the indicated timeStep
@pre destinationLabelVector must contain all indicated destinationLabels for mapping.*/
MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector,
const TimeStepType timeStep, mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE,
mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE,
bool destinationBackgroundLocked = false,
LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label. Function processes the whole image volume for all time steps.
For more details please see TransferLabelContentAtTimeStep.
@sa TransferLabelContentAtTimeStep*/
MITKMULTILABEL_EXPORT void TransferLabelContent(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector,
mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE,
mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE,
bool destinationBackgroundLocked = false,
LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.h b/Modules/Multilabel/mitkLabelSetImageConverter.h
index 349560bc4e..6b712a3f80 100644
--- a/Modules/Multilabel/mitkLabelSetImageConverter.h
+++ b/Modules/Multilabel/mitkLabelSetImageConverter.h
@@ -1,76 +1,76 @@
/*============================================================================
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 mitkLabelSetImageConverter_h
#define mitkLabelSetImageConverter_h
#include <mitkLabelSetImage.h>
namespace mitk
{
/**
* \brief Convert mitk::LabelSetImage to mitk::Image (itk::VectorImage)
*/
MITKMULTILABEL_EXPORT Image::Pointer ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage);
/**
* \brief Convert mitk::Image to mitk::LabelSetImage, templating and differentation between itk::Image and
* itk::VectorImage is internal
*/
MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageToLabelSetImage(Image::Pointer image);
MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageVectorToLabelSetImage(const std::vector<mitk::Image::Pointer>& images, const TimeGeometry* timeGeometry);
MITKMULTILABEL_EXPORT std::vector<mitk::Image::Pointer> SplitVectorImage(const Image* vecImage);
/** Function takes a vector of labels and transfers all labels as clones with adapted label values to the result vector.
The values will be adapted according to the provided mapping (key is the old value, value the new).
- @remark: Only labels will be transfered, nothing else. So things like message observers or m_ReservedLabelValuesFunctor must be copied explicitly.*/
+ @remark: Only labels will be transferred, nothing else. So things like message observers or m_ReservedLabelValuesFunctor must be copied explicitly.*/
MITKMULTILABEL_EXPORT LabelSetImage::LabelVectorType GenerateLabelSetWithMappedValues(const LabelSetImage::ConstLabelVectorType&, LabelValueMappingVector labelMapping);
MITKMULTILABEL_EXPORT Image::Pointer ConvertImageToGroupImage(const Image* inputImage, mitk::LabelSetImage::LabelValueVectorType& foundLabels);
MITKMULTILABEL_EXPORT bool CheckForLabelValueConflictsAndResolve(const mitk::LabelSetImage::LabelValueVectorType& newValues, mitk::LabelSetImage::LabelValueVectorType& usedLabelValues, mitk::LabelSetImage::LabelValueVectorType& correctedLabelValues);
/** Function creates a binary mask representing only the specified label of the multi label segmentation.
* @param segmentation Pointer to the segmentation that is the source for the mask.
* @param labelValue the label that should be extracted.
* @param createBinaryMap indicates if the label pixels should be indicated by the value 1 (createBinaryMap==true) or by the value of the label
* (createBinaryMap==false).
* @pre segmentation must point to a valid instance.
* @pre labelValue must exist in segmentation.*/
MITKMULTILABEL_EXPORT Image::Pointer CreateLabelMask(const LabelSetImage* segmentation, LabelSetImage::LabelValueType labelValue, bool createBinaryMap = true);
/** Function creates a map of all label classes in a specified group.
* @param segmentation Pointer to the segmentation that is the source for the map.
* @param groupID the group that should be used.
* @param selectedLabels The selected labels that should be represented in the class map. This is meant as white list, therefore only
* label values listed in the list are used. Invalid label values (not existing in the group) will be ignored.
* @return Returns a pair where first is the pointer to the created map image and second is the look up table that indicated
* the pixel value of each found class in the map.
* @pre segmentation must point to a valid instance.
* @pre groupID must exist in segmentation.*/
using IDToLabelClassNameMapType = std::map<LabelSetImage::LabelValueType, std::string>;
MITKMULTILABEL_EXPORT std::pair<Image::Pointer, IDToLabelClassNameMapType> CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID, const LabelSetImage::LabelValueVectorType& selectedLabels);
/** Function creates a map of all label classes in a specified group.
* @overload
* This version always uses all labels of a group.
* @param segmentation Pointer to the segmentation that is the source for the map.
* @param groupID the group that should be used.
* @return Returns a pair where first is the pointer to the created map image and second is the look up table that indicated
* the pixel value of each found class in the map.
* @pre segmentation must point to a valid instance.
* @pre groupID must exist in segmentation.*/
MITKMULTILABEL_EXPORT std::pair<Image::Pointer, IDToLabelClassNameMapType> CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID);
}
#endif
diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
index 4defba4876..4dc7bdff26 100644
--- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
+++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
@@ -1,233 +1,233 @@
/*============================================================================
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 mitkLabelSetImageVtkMapper2D_h
#define mitkLabelSetImageVtkMapper2D_h
// MITK
#include "MitkMultilabelExports.h"
#include "mitkCommon.h"
// MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkExtractSliceFilter.h"
#include "mitkLabelSetImage.h"
#include "mitkVtkMapper.h"
// VTK
#include <vtkSmartPointer.h>
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageReslice;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkNeverTranslucentTexture;
class vtkImageMapToColors;
namespace mitk
{
/** \brief Mapper to resample and display 2D slices of a 3D labelset image.
*
* Properties that can be set for labelset images and influence this mapper are:
*
* - \b "labelset.contour.active": (BoolProperty) whether to show only the active label as a contour or not
* - \b "labelset.contour.width": (FloatProperty) line width of the contour
* The default properties are:
* - \b "labelset.contour.active", mitk::BoolProperty::New( true ), renderer, overwrite )
* - \b "labelset.contour.width", mitk::FloatProperty::New( 2.0 ), renderer, overwrite )
* \ingroup Mapper
*/
class MITKMULTILABEL_EXPORT LabelSetImageVtkMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro(LabelSetImageVtkMapper2D, VtkMapper);
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** \brief Get the Image to map */
const mitk::Image *GetInput(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer *renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKMULTILABEL_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
vtkSmartPointer<vtkPropAssembly> m_Actors;
/** Vector containing the pointer of the currently used group images.
* IMPORTANT: This member must not be used to access any data.
* Its purpose is to allow checking if the order of the groups has changed
* in order to adapt the pipe line accordingly*/
std::vector<const Image*> m_GroupImageIDs;
std::vector<vtkSmartPointer<vtkActor>> m_LayerActorVector;
std::vector<vtkSmartPointer<vtkPolyDataMapper>> m_LayerMapperVector;
std::vector<vtkSmartPointer<vtkImageData>> m_ReslicedImageVector;
std::vector<vtkSmartPointer<vtkImageMapToColors>> m_LayerImageMapToColors;
std::vector<vtkSmartPointer<vtkNeverTranslucentTexture>> m_LayerTextureVector;
vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
vtkSmartPointer<vtkPlaneSource> m_Plane;
std::vector<mitk::ExtractSliceFilter::Pointer> m_ReslicerVector;
vtkSmartPointer<vtkPolyData> m_OutlinePolyData;
/** \brief An actor for the outline */
vtkSmartPointer<vtkActor> m_OutlineActor;
/** \brief An actor for the outline shadow*/
vtkSmartPointer<vtkActor> m_OutlineShadowActor;
/** \brief A mapper for the outline */
vtkSmartPointer<vtkPolyDataMapper> m_OutlineMapper;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastDataUpdateTime;
/** \brief Timestamp of last update of a property. */
itk::TimeStamp m_LastPropertyUpdateTime;
/** \brief Timestamp of last update of a property. */
itk::TimeStamp m_LastActiveLabelUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType *m_mmPerPixel;
/** look up table for label colors. */
mitk::LookupTable::Pointer m_LabelLookupTable;
mitk::PlaneGeometry::Pointer m_WorldPlane;
bool m_HasValidContent;
mitk::TimeStepType m_LastTimeStep;
unsigned int m_NumberOfLayers;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default destructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage *GetLocalStorage(mitk::BaseRenderer *renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
/** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function).
* Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
*/
void ApplyRenderingMode(mitk::BaseRenderer *renderer);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer *renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]);
/** \brief Generates a vtkPolyData object containing the outline of a given binary slice.
\param renderer Pointer to the renderer containing the needed information
\param image
\param pixelValue
\note This code is based on code from the iil library.
*/
vtkSmartPointer<vtkPolyData> CreateOutlinePolyData(mitk::BaseRenderer *renderer,
vtkImageData *image,
int pixelValue = 1);
/** Default constructor */
LabelSetImageVtkMapper2D();
/** Default deconstructor */
~LabelSetImageVtkMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
void GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector<mitk::LabelSetImage::GroupIndexType>& outdatedGroupIDs);
void GenerateActiveLabelOutline(mitk::BaseRenderer* renderer);
/** \brief Generates the look up table that should be used.
*/
void GenerateLookupTable(mitk::BaseRenderer* renderer);
/** \brief This method uses the vtkCamera clipping range and the layer property
- * to calcualte the depth of the object (e.g. image or contour). The depth is used
+ * to calculate the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer *renderer);
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given Geometry2D intersects the given
* SlicedGeometry3D. It calculates the distance of the Geometry2D to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
**/
bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, const BaseGeometry* imageGeometry) const;
};
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkMultiLabelIOHelper.h b/Modules/Multilabel/mitkMultiLabelIOHelper.h
index 0345aa2fcb..f1d2c0c221 100644
--- a/Modules/Multilabel/mitkMultiLabelIOHelper.h
+++ b/Modules/Multilabel/mitkMultiLabelIOHelper.h
@@ -1,127 +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.
============================================================================*/
#ifndef mitkMultiLabelIOHelper_h
#define mitkMultiLabelIOHelper_h
#include <mitkLabel.h>
#include <itkSmartPointer.h>
#include <nlohmann/json.hpp>
#include <MitkMultilabelExports.h>
namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
namespace itk
{
class MetaDataDictionary;
}
namespace mitk
{
class LabelSetImage;
const constexpr char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
const constexpr char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
const constexpr char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
const constexpr char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
const constexpr char* const PROPERTY_KEY_UID = "org_mitk_uid";
/**
* @brief The MultiLabelIOHelper is a static helper class that supports serialization of mitk::LabelSetImage
*
* This class provides static functions for converting mitk::Label into XML and also allows the serialization
* of mitk::LabelSet as presets
*/
class MITKMULTILABEL_EXPORT MultiLabelIOHelper
{
public:
/**
* @brief Saves the mitk::LabelSet configuration of inputImage to presetFilename.
* The preset is stored as "*.lsetp"
* @param presetFilename the filename including the filesystem path
* @param inputImage the input image from which the preset should be generated
* @return true if the serialization was successful and false otherwise
*/
static bool SaveLabelSetImagePreset(const std::string &presetFilename,
const mitk::LabelSetImage *inputImage);
/**
* @brief Loads an existing preset for a mitk::LabelSetImage from presetFilename and applies it to inputImage
* @param presetFilename the filename of the preset including the filesystem path
* @param inputImage the image to which the loaded preset will be applied
* @return true if the deserilization was successful and false otherwise
*/
static bool LoadLabelSetImagePreset(const std::string &presetFilename,
mitk::LabelSetImage *inputImage);
/**
* @brief Creates a mitk::Label from an XML element
* @param labelElem the xml element from which a mitk::Label will be created
* @return the created mitk::Label
*/
static itk::SmartPointer<mitk::Label> LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem);
/**
* @brief Creates an XML element from a mitk::Label
* @param doc
* @param label the mitk::Label from which the xml element will be created
* @return the created XML element
*/
static tinyxml2::XMLElement *GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, const Label *label);
/**
- * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into
+ * @brief Since a mitk::Label is basically a mitk::PropertyList this function converts the label's properties into
* XML
* @param doc
* @param key the property's key which will be used in the XML element
* @param property the mitk::BaseProperty that should be converted
* @return the created XML element
*/
static tinyxml2::XMLElement *PropertyToXMLElement(tinyxml2::XMLDocument& doc, const std::string &key, const BaseProperty *property);
/**
- * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts a XML element into a property
+ * @brief Since a mitk::Label is basically a mitk::PropertyList this function converts an XML element into a property
* @param key the property's key
* @param prop the mitk::BaseProperty that will be created
* @param elem the XML elem from which the property will be created
* @return true if the conversion was successful and false otherwise
*/
static bool PropertyFromXMLElement(std::string &key, itk::SmartPointer<mitk::BaseProperty> &prop, const tinyxml2::XMLElement *elem);
/** Helper that extracts the value of a key in a meta dictionary as int.
* If the key does not exist 0 is returned.*/
static int GetIntByKey(const itk::MetaDataDictionary& dic, const std::string& key);
/** Helper that extracts the value of a key in a meta dictionary as string.
* If the key does not exist an empty string is returned.*/
static std::string GetStringByKey(const itk::MetaDataDictionary& dic, const std::string& key);
static nlohmann::json SerializeMultLabelGroupsToJSON(const mitk::LabelSetImage* inputImage);
static std::vector<LabelVector> DeserializeMultiLabelGroupsFromJSON(const nlohmann::json& listOfLabelSets);
static nlohmann::json SerializeLabelToJSON(const Label* label);
static mitk::Label::Pointer DeserializeLabelFromJSON(const nlohmann::json& labelJson);
private:
MultiLabelIOHelper();
};
}
#endif
diff --git a/Modules/Multilabel/mitkSegmentationTaskList.cpp b/Modules/Multilabel/mitkSegmentationTaskList.cpp
index 1755207f6a..33f130a6e5 100644
--- a/Modules/Multilabel/mitkSegmentationTaskList.cpp
+++ b/Modules/Multilabel/mitkSegmentationTaskList.cpp
@@ -1,196 +1,196 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkSegmentationTaskList.h"
#include <mitkIOUtil.h>
#include <mitkProperties.h>
mitk::SegmentationTaskList::Task::Task()
: m_Defaults(nullptr)
{
}
mitk::SegmentationTaskList::Task::~Task()
{
}
void mitk::SegmentationTaskList::Task::SetDefaults(const Task* defaults)
{
m_Defaults = defaults;
}
mitk::SegmentationTaskList::SegmentationTaskList()
{
// A base data cannot be serialized if empty. To be not considered empty its
// geometry must consist of at least one time step. However, a segmentation
- // task would then appear as invisible spacial object in a scene. This can
+ // task would then appear as invisible spatial object in a scene. This can
// be prevented by excluding it from the scene's bounding box calculations.
this->GetTimeGeometry()->Expand(1);
this->SetProperty("includeInBoundingBox", BoolProperty::New(false));
}
mitk::SegmentationTaskList::SegmentationTaskList(const Self& other)
: BaseData(other)
{
}
mitk::SegmentationTaskList::~SegmentationTaskList()
{
}
size_t mitk::SegmentationTaskList::GetNumberOfTasks() const
{
return m_Tasks.size();
}
size_t mitk::SegmentationTaskList::AddTask(const Task& subtask)
{
m_Tasks.push_back(subtask);
m_Tasks.back().SetDefaults(&m_Defaults);
return m_Tasks.size() - 1;
}
const mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index) const
{
return &m_Tasks.at(index);
}
mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index)
{
return &m_Tasks.at(index);
}
const mitk::SegmentationTaskList::Task& mitk::SegmentationTaskList::GetDefaults() const
{
return m_Defaults;
}
void mitk::SegmentationTaskList::SetDefaults(const Task& defaults)
{
m_Defaults = defaults;
for (auto& subtask : m_Tasks)
subtask.SetDefaults(&m_Defaults);
}
bool mitk::SegmentationTaskList::IsDone() const
{
for (size_t i = 0; i < m_Tasks.size(); ++i)
{
if (!this->IsDone(i))
return false;
}
return true;
}
bool mitk::SegmentationTaskList::IsDone(size_t index) const
{
return std::filesystem::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult()));
}
std::filesystem::path mitk::SegmentationTaskList::GetInputLocation() const
{
std::string inputLocation;
this->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation);
return !inputLocation.empty()
? std::filesystem::path(inputLocation).lexically_normal()
: std::filesystem::path();
}
std::filesystem::path mitk::SegmentationTaskList::GetBasePath() const
{
return this->GetInputLocation().remove_filename();
}
std::filesystem::path mitk::SegmentationTaskList::GetAbsolutePath(const std::filesystem::path& path) const
{
if (path.empty())
return path;
auto normalizedPath = path.lexically_normal();
return !normalizedPath.is_absolute()
? this->GetBasePath() / normalizedPath
: normalizedPath;
}
std::filesystem::path mitk::SegmentationTaskList::GetInterimPath(const std::filesystem::path& path) const
{
if (path.empty() || !path.has_filename())
return path;
auto interimPath = path;
return interimPath.replace_extension(".interim" + path.extension().string());
}
void mitk::SegmentationTaskList::SaveTask(size_t index, const BaseData* segmentation, bool saveAsInterimResult)
{
if (segmentation == nullptr)
return;
auto path = this->GetAbsolutePath(this->GetResult(index));
auto interimPath = this->GetInterimPath(path);
if (std::filesystem::exists(path))
saveAsInterimResult = false;
IOUtil::Save(segmentation, saveAsInterimResult
? interimPath.string()
: path.string());
if (!saveAsInterimResult && std::filesystem::exists(interimPath))
{
std::error_code ec;
std::filesystem::remove(interimPath, ec);
}
}
std::vector<mitk::SegmentationTaskList::Task>::const_iterator mitk::SegmentationTaskList::begin() const
{
return m_Tasks.begin();
}
std::vector<mitk::SegmentationTaskList::Task>::const_iterator mitk::SegmentationTaskList::end() const
{
return m_Tasks.end();
}
std::vector<mitk::SegmentationTaskList::Task>::iterator mitk::SegmentationTaskList::begin()
{
return m_Tasks.begin();
}
std::vector<mitk::SegmentationTaskList::Task>::iterator mitk::SegmentationTaskList::end()
{
return m_Tasks.end();
}
void mitk::SegmentationTaskList::SetRequestedRegionToLargestPossibleRegion()
{
}
bool mitk::SegmentationTaskList::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::SegmentationTaskList::VerifyRequestedRegion()
{
return true;
}
void mitk::SegmentationTaskList::SetRequestedRegion(const itk::DataObject*)
{
}
diff --git a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp
index d871108746..31a8b46481 100644
--- a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp
+++ b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp
@@ -1,890 +1,890 @@
/*============================================================================
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.
============================================================================*/
// std includes
#include <string>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageCast.h>
#include <mitkPixelBasedParameterFitImageGenerator.h>
#include <mitkROIBasedParameterFitImageGenerator.h>
#include <mitkModelFitInfo.h>
#include <mitkModelFitCmdAppsHelper.h>
#include <mitkMaskedDynamicImageStatisticsGenerator.h>
#include <mitkLevenbergMarquardtModelFitFunctor.h>
#include <mitkNormalizedSumOfSquaredDifferencesFitCostFunction.h>
#include <mitkExtractTimeGrid.h>
#include <mitkAterialInputFunctionGenerator.h>
#include <mitkModelFitResultHelper.h>
#include <mitkDescriptivePharmacokineticBrixModelParameterizer.h>
#include <mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h>
#include <mitkDescriptivePharmacokineticBrixModelFactory.h>
#include <mitkExtendedToftsModelParameterizer.h>
#include <mitkExtendedToftsModelFactory.h>
#include <mitkTwoCompartmentExchangeModelParameterizer.h>
#include <mitkTwoCompartmentExchangeModelFactory.h>
#include <mitkThreeStepLinearModelParameterizer.h>
#include <mitkThreeStepLinearModelFactory.h>
#include <mitkTwoStepLinearModelParameterizer.h>
#include <mitkTwoStepLinearModelFactory.h>
#include <mitkModelFactoryBase.h>
std::string inFilename;
std::string outFileName;
std::string maskFileName;
std::string aifMaskFileName;
std::string aifImageFileName;
mitk::Image::Pointer image;
mitk::Image::Pointer mask;
mitk::Image::Pointer aifImage;
mitk::Image::Pointer aifMask;
bool useConstraints(false);
bool verbose(false);
bool roibased(false);
bool preview(false);
std::string modelName;
float aifHematocritLevel(0);
float brixInjectionTime(0);
const std::string MODEL_NAME_2SL = "2SL";
const std::string MODEL_NAME_3SL = "3SL";
const std::string MODEL_NAME_descriptive = "descriptive";
const std::string MODEL_NAME_tofts = "tofts";
const std::string MODEL_NAME_2CX = "2CX";
void onFitEvent(::itk::Object* caller, const itk::EventObject & event, void* /*data*/)
{
itk::ProgressEvent progressEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast<mitk::ParameterFitImageGeneratorBase*>(caller);
std::cout <<castedReporter->GetProgress()*100 << "% ";
}
}
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Dynamic Data Analysis Tools");
parser.setTitle("MR Perfusion");
parser.setDescription("MiniApp that allows to fit MRI perfusion models and generates the according parameter maps. IMPORTANT!!!: The app assumes that the input images (signal and AIF) are concentration images. If your images do not hold this assumption, convert the image date before using this app (e.g. by using the signal-to-concentration-converter mini app.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Model parameters");
parser.addArgument(
"model", "l", mitkCommandLineParser::String, "Model function", "Model that should be used to fit the concentration signal. Options are: \""+MODEL_NAME_descriptive+"\" (descriptive pharmacokinetic Brix model),\"" + MODEL_NAME_2SL + "\" (two step linear model),\""+MODEL_NAME_3SL+"\" (three step linear model), \""+MODEL_NAME_tofts+"\" (extended tofts model) or \""+MODEL_NAME_2CX+"\" (two compartment exchange model).", us::Any(std::string(MODEL_NAME_tofts)));
parser.addArgument(
"injectiontime", "j", mitkCommandLineParser::Float, "Injection time [min]", "Injection time of the bolus. This information is needed for the descriptive pharmacokinetic Brix model.", us::Any());
parser.endGroup();
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file template",
"where to save the output parameter images. The specified path will be used as template to determine the format (via extension) and the name \"root\". For each parameter a suffix will be added to the name.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("AIF parameters");
parser.addArgument(
"aifmask", "n", mitkCommandLineParser::File, "AIF mask file", "Mask that defines the spatial image region that should be used as AIF for models that need one. Must have the same geometry as the AIF input image!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "aifimage", "a", mitkCommandLineParser::File, "AIF image file", "3D+t image that defines the image that containes the AIF signal. If this flag is not set and the model needs a AIF, the CLI will assume that the AIF is encoded in the normal image. Must have the same geometry as the AIF mask!", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "aifimage", "a", mitkCommandLineParser::File, "AIF image file", "3D+t image that defines the image that contains the AIF signal. If this flag is not set and the model needs a AIF, the CLI will assume that the AIF is encoded in the normal image. Must have the same geometry as the AIF mask!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"hematocrit", "h", mitkCommandLineParser::Float, "Hematocrit Level", "Value needed for correct AIF computation. Only needed if model needs an AIF. Default value is 0.45.", us::Any(0.45));
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be fitted. Must have the same geometry as the input image!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output");
parser.addArgument(
- "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intesity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
+ "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intensity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
parser.addArgument(
- "constraints", "c", mitkCommandLineParser::Bool, "Constraints", "Indicates if constraints should be used for the fitting (if flag is set the default contraints will be used.).", us::Any(false));
+ "constraints", "c", mitkCommandLineParser::Bool, "Constraints", "Indicates if constraints should be used for the fitting (if flag is set the default constraints will be used.).", us::Any(false));
parser.addArgument(
"preview", "p", mitkCommandLineParser::Bool, "Preview outputs", "The application previews the outputs (filename, type) it would produce with the current settings.");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
// parse, cast and set required arguments
modelName = MODEL_NAME_tofts;
if (parsedArgs.count("model"))
{
modelName = us::any_cast<std::string>(parsedArgs["model"]);
}
inFilename = us::any_cast<std::string>(parsedArgs["input"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("mask"))
{
maskFileName = us::any_cast<std::string>(parsedArgs["mask"]);
}
if (parsedArgs.count("aifimage"))
{
aifImageFileName = us::any_cast<std::string>(parsedArgs["aifimage"]);
}
if (parsedArgs.count("aifmask"))
{
aifMaskFileName = us::any_cast<std::string>(parsedArgs["aifmask"]);
}
verbose = false;
if (parsedArgs.count("verbose"))
{
verbose = us::any_cast<bool>(parsedArgs["verbose"]);
}
preview = false;
if (parsedArgs.count("preview"))
{
preview = us::any_cast<bool>(parsedArgs["preview"]);
}
roibased = false;
if (parsedArgs.count("roibased"))
{
roibased = us::any_cast<bool>(parsedArgs["roibased"]);
}
useConstraints = false;
if (parsedArgs.count("constraints"))
{
useConstraints = us::any_cast<bool>(parsedArgs["constraints"]);
}
aifHematocritLevel = 0.45;
if (parsedArgs.count("hematocrit"))
{
aifHematocritLevel = us::any_cast<float>(parsedArgs["hematocrit"]);
}
brixInjectionTime = 0.0;
if (parsedArgs.count("injectiontime"))
{
brixInjectionTime = us::any_cast<float>(parsedArgs["injectiontime"]);
}
return true;
}
mitk::ModelFitFunctorBase::Pointer createDefaultFitFunctor(
const mitk::ModelParameterizerBase* parameterizer, const mitk::ModelFactoryBase* modelFactory)
{
mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor =
mitk::LevenbergMarquardtModelFitFunctor::New();
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 =
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New();
fitFunctor->RegisterEvaluationParameter("Chi^2", chi2);
if (useConstraints)
{
fitFunctor->SetConstraintChecker(modelFactory->CreateDefaultConstraints().GetPointer());
}
mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel();
::itk::LevenbergMarquardtOptimizer::ScalesType scales;
scales.SetSize(refModel->GetNumberOfParameters());
scales.Fill(1.0);
fitFunctor->SetScales(scales);
fitFunctor->SetDebugParameterMaps(true);
return fitFunctor.GetPointer();
}
/**Helper that ensures that the mask (if it exists) is always 3D image. If the mask is originally an 4D image, the first
time step will be used.*/
mitk::Image::Pointer getMask3D()
{
mitk::Image::Pointer result;
if (mask.IsNotNull())
{
result = mask;
//mask settings
if (mask->GetTimeSteps() > 1)
{
MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit.";
mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New();
maskedImageTimeSelector->SetInput(mask);
maskedImageTimeSelector->SetTimeNr(0);
maskedImageTimeSelector->UpdateLargestPossibleRegion();
result = maskedImageTimeSelector->GetOutput();
}
}
return result;
}
void getAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif,
mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid)
{
if (aifMask.IsNotNull())
{
aif.clear();
aifTimeGrid.clear();
mitk::AterialInputFunctionGenerator::Pointer aifGenerator =
mitk::AterialInputFunctionGenerator::New();
//Hematocrit level
aifGenerator->SetHCL(aifHematocritLevel);
std::cout << "AIF hematocrit level: " << aifHematocritLevel << std::endl;
mitk::Image::Pointer selectedAIFMask = aifMask;
//mask settings
if (aifMask->GetTimeSteps() > 1)
{
MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit.";
mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New();
maskedImageTimeSelector->SetInput(aifMask);
maskedImageTimeSelector->SetTimeNr(0);
maskedImageTimeSelector->UpdateLargestPossibleRegion();
aifMask = maskedImageTimeSelector->GetOutput();
}
aifGenerator->SetMask(aifMask);
mitk::Image::Pointer selectedAIFImage = image;
//image settings
if (aifImage.IsNotNull())
{
selectedAIFImage = aifImage;
}
aifGenerator->SetDynamicImage(selectedAIFImage);
aif = aifGenerator->GetAterialInputFunction();
aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid();
}
else
{
mitkThrow() << "Cannot generate AIF. AIF mask was not specified or correctly loaded.";
}
}
void generateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer =
mitk::DescriptivePharmacokineticBrixModelParameterizer::New();
mitk::Image::Pointer mask3D = getMask3D();
//Model configuration (static parameters) can be done now
modelParameterizer->SetTau(brixInjectionTime);
std::cout << "Injection time [min]: " << brixInjectionTime << std::endl;
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(image);
imageTimeSelector->SetTimeNr(0);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage;
mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage);
modelParameterizer->SetBaseImage(baseImage);
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
std::string roiUID = "";
if (mask3D.IsNotNull())
{
fitGenerator->SetMask(mask3D);
roiUID = mask->GetUID();
}
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID);
}
void generateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::Image::Pointer mask3D = getMask3D();
if (mask3D.IsNull())
{
return;
}
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer =
mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New();
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask3D);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Model configuration (static parameters) can be done now
modelParameterizer->SetTau(brixInjectionTime);
std::cout << "Injection time [min]: " << brixInjectionTime << std::endl;
modelParameterizer->SetBaseValue(roiSignal[0]);
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask3D);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
std::string roiUID = mask->GetUID();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
roiSignal.begin(); pos != roiSignal.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("ROI", infoSignal);
}
template <typename TParameterizer, typename TFactory>
void GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer = TParameterizer::New();
mitk::Image::Pointer mask3D = getMask3D();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
std::string roiUID = "";
if (mask3D.IsNotNull())
{
fitGenerator->SetMask(mask3D);
roiUID = mask->GetUID();
}
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID);
}
template <typename TParameterizer, typename TFactory>
void GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::Image::Pointer mask3D = getMask3D();
if (mask3D.IsNull())
{
return;
}
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer = TParameterizer::New();
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask3D);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask3D);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
std::string roiUID = mask->GetUID();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
roiSignal.begin(); pos != roiSignal.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("ROI", infoSignal);
}
template <typename TParameterizer, typename TFactory>
void generateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
mitk::AIFBasedModelBase::AterialInputFunctionType aif;
mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid;
getAIF(aif, aifTimeGrid);
modelParameterizer->SetAIF(aif);
modelParameterizer->SetAIFTimeGrid(aifTimeGrid);
mitk::Image::Pointer mask3D = getMask3D();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
std::string roiUID = "";
if (mask3D.IsNotNull())
{
fitGenerator->SetMask(mask3D);
roiUID = mask->GetUID();
}
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(),
roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos =
aif.begin(); pos != aif.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("AIF", infoSignal);
}
template <typename TParameterizer, typename TFactory>
void generateAIFbasedModelFit_ROIBased(
mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo,
mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::Image::Pointer mask3D = getMask3D();
if (mask3D.IsNull())
{
return;
}
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
mitk::AIFBasedModelBase::AterialInputFunctionType aif;
mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid;
getAIF(aif, aifTimeGrid);
modelParameterizer->SetAIF(aif);
modelParameterizer->SetAIFTimeGrid(aifTimeGrid);
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask3D);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask3D);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
std::string roiUID = mask->GetUID();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(),
roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
roiSignal.begin(); pos != roiSignal.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("ROI", infoSignal);
infoSignal.clear();
for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos =
aif.begin(); pos != aif.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("AIF", infoSignal);
}
void storeResultImage(const std::string& name, mitk::Image* image, mitk::modelFit::Parameter::Type nodeType, const mitk::modelFit::ModelFitInfo* modelFitInfo)
{
mitk::modelFit::SetModelFitDataProperties(image, name, nodeType, modelFitInfo);
std::string ext = ::itksys::SystemTools::GetFilenameLastExtension(outFileName);
std::string dir = itksys::SystemTools::GetFilenamePath(outFileName);
dir = itksys::SystemTools::ConvertToOutputPath(dir);
std::string rootName = itksys::SystemTools::GetFilenameWithoutLastExtension(outFileName);
std::string fileName = rootName + "_" + name + ext;
std::vector<std::string> pathElements;
pathElements.push_back(dir);
pathElements.push_back(fileName);
std::string fullOutPath = itksys::SystemTools::ConvertToOutputPath(dir + "/" + fileName);
mitk::IOUtil::Save(image, fullOutPath);
std::cout << "Store result (parameter: "<<name<<"): " << fullOutPath << std::endl;
}
void createFitGenerator(mitk::modelFit::ModelFitInfo::Pointer& fitSession, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
bool isDescBrixFactory = modelName == MODEL_NAME_descriptive;
bool isToftsFactory = modelName == MODEL_NAME_tofts;
bool is2CXMFactory = modelName == MODEL_NAME_2CX;
bool is3SLFactory = modelName == MODEL_NAME_3SL;
bool is2SLFactory = modelName == MODEL_NAME_2SL;
if (isDescBrixFactory)
{
std::cout << "Model: descriptive pharmacokinetic brix model" << std::endl;
if (!roibased)
{
generateDescriptiveBrixModel_PixelBased(fitSession, generator);
}
else
{
generateDescriptiveBrixModel_ROIBased(fitSession, generator);
}
}
else if (is3SLFactory)
{
std::cout << "Model: three step linear model" << std::endl;
if (!roibased)
{
GenerateLinearModelFit_PixelBased<mitk::ThreeStepLinearModelParameterizer, mitk::ThreeStepLinearModelFactory>(fitSession, generator);
}
else
{
GenerateLinearModelFit_ROIBased<mitk::ThreeStepLinearModelParameterizer, mitk::ThreeStepLinearModelFactory>(fitSession, generator);
}
}
else if (is2SLFactory)
{
std::cout << "Model: two step linear model" << std::endl;
if (!roibased)
{
GenerateLinearModelFit_PixelBased<mitk::TwoStepLinearModelParameterizer, mitk::TwoStepLinearModelFactory>(fitSession, generator);
}
else
{
GenerateLinearModelFit_ROIBased<mitk::TwoStepLinearModelParameterizer, mitk::TwoStepLinearModelFactory>(fitSession, generator);
}
}
else if (isToftsFactory)
{
std::cout << "Model: extended tofts model" << std::endl;
if (!roibased)
{
generateAIFbasedModelFit_PixelBased<mitk::ExtendedToftsModelParameterizer, mitk::ExtendedToftsModelFactory>(fitSession, generator);
}
else
{
generateAIFbasedModelFit_ROIBased<mitk::ExtendedToftsModelParameterizer, mitk::ExtendedToftsModelFactory>(fitSession, generator);
}
}
else if (is2CXMFactory)
{
std::cout << "Model: two compartment exchange model" << std::endl;
if (!roibased)
{
generateAIFbasedModelFit_PixelBased<mitk::TwoCompartmentExchangeModelParameterizer, mitk::TwoCompartmentExchangeModelFactory>(fitSession, generator);
}
else
{
generateAIFbasedModelFit_ROIBased<mitk::TwoCompartmentExchangeModelParameterizer, mitk::TwoCompartmentExchangeModelFactory>(fitSession, generator);
}
}
else
{
std::cerr << "ERROR. Model flag is unknown. Given flag: " << modelName << std::endl;
}
}
void doFitting()
{
mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;
::itk::CStyleCommand::Pointer command = ::itk::CStyleCommand::New();
command->SetCallback(onFitEvent);
createFitGenerator(fitSession, generator);
if (generator.IsNotNull() )
{
std::cout << "Started fitting process..." << std::endl;
generator->AddObserver(::itk::AnyEvent(), command);
generator->Generate();
std::cout << std::endl << "Finished fitting process" << std::endl;
mitk::storeModelFitGeneratorResults(outFileName, generator, fitSession);
}
else
{
mitkThrow() << "Fitting error! Could not initialize fitting job.";
}
}
void doPreview()
{
mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;
createFitGenerator(fitSession, generator);
if (generator.IsNotNull())
{
mitk::previewModelFitGeneratorResults(outFileName, generator);
}
else
{
mitkThrow() << "Fitting error! Could not initialize fitting job.";
}
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
//! [do processing]
try
{
image = mitk::IOUtil::Load<mitk::Image>(inFilename, &readerFilterFunctor);
std::cout << "Input: " << inFilename << std::endl;
if (!maskFileName.empty())
{
mask = mitk::IOUtil::Load<mitk::Image>(maskFileName, &readerFilterFunctor);
std::cout << "Mask: " << maskFileName << std::endl;
}
else
{
std::cout << "Mask: none" << std::endl;
}
if (modelName != MODEL_NAME_descriptive && modelName != MODEL_NAME_3SL && MODEL_NAME_2SL != modelName)
{
if (!aifMaskFileName.empty())
{
aifMask = mitk::IOUtil::Load<mitk::Image>(aifMaskFileName, &readerFilterFunctor);
std::cout << "AIF mask: " << aifMaskFileName << std::endl;
}
else
{
- mitkThrow() << "Error. Cannot fit. Choosen model needs an AIF. Please specify AIF mask (--aifmask).";
+ mitkThrow() << "Error. Cannot fit. Chosen model needs an AIF. Please specify AIF mask (--aifmask).";
}
if (!aifImageFileName.empty())
{
aifImage = mitk::IOUtil::Load<mitk::Image>(aifImageFileName, &readerFilterFunctor);
std::cout << "AIF image: " << aifImageFileName << std::endl;
}
else
{
std::cout << "AIF image: none (using signal image)" << std::endl;
}
}
if (roibased && mask.IsNull())
{
mitkThrow() << "Error. Cannot fit. Please specify mask if you select roi based fitting.";
}
std::cout << "Style: ";
if (roibased)
{
std::cout << "ROI based";
}
else
{
std::cout << "pixel based";
}
std::cout << std::endl;
if (preview)
{
doPreview();
}
else
{
doFitting();
}
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const itk::ExceptionObject& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h b/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h
index b38ee0fb54..44eea51db6 100644
--- a/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h
+++ b/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h
@@ -1,107 +1,107 @@
/*============================================================================
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 mitkAterialInputFunctionGenerator_h
#define mitkAterialInputFunctionGenerator_h
#include <mitkImage.h>
#include "mitkAIFBasedModelBase.h"
#include "MitkPharmacokineticsExports.h"
namespace mitk
{
/** \class AterialInputFunctionGenerator
* \brief Compute the Aterial Input Function from a given dynamic image and a mask defining the tumour supplying artery
*
* The AterialInputFunctionGenerator takes a given 4D dynamic image and a corresponding mask and returns an array of the averaged values and
* an array of the corresponding TimeGrid
* within the mask over time. No conversion is performed, so conversion from signal to concentration has to be performed in advanced
* and the resulting image is fed into the Generator.
- * The generator checks wether both image and mask are set and passes them to the itkMaskedNaryStatisticsImageFilter and the mitkExtractTimeGrid, to
+ * The generator checks whether both image and mask are set and passes them to the itkMaskedNaryStatisticsImageFilter and the mitkExtractTimeGrid, to
* calculate the mean of every time slice within the ROI and extract the corresponding time grid from the date set.
*/
class MITKPHARMACOKINETICS_EXPORT AterialInputFunctionGenerator : public itk::Object
{
public:
mitkClassMacroItkParent(AterialInputFunctionGenerator, itk::Object);
itkNewMacro(Self);
/** @brief Setter and Getter for Input Image for calculation of AIF, already converted to concentrations
* Getter calls CheckValidInputs() and CalculateAIFAndGetResult() if HasOutdatedResults() is true */
itkSetConstObjectMacro(DynamicImage, Image);
itkGetConstObjectMacro(DynamicImage, Image);
/** @brief Setter and Getter for mask defining the tumour feeding atery
* Getter calls CheckValidInputs() and CalculateAIFAndGetResult() if HasOutdatedResults() is true */
itkSetConstObjectMacro(Mask, Image);
itkGetConstObjectMacro(Mask, Image);
/** @brief Setter and Getter for the hematocritlevel, important for conversion to plasma curve*/
itkSetMacro(HCL, double);
itkGetConstReferenceMacro(HCL, double);
//Common Value for Hematocrit level is 0.45
static const double DEFAULT_HEMATOCRIT_LEVEL;
void SetDefaultHematocritLevel()
{
this->m_HCL = DEFAULT_HEMATOCRIT_LEVEL;
};
double GetDefaultHematocritLevel()
{
return DEFAULT_HEMATOCRIT_LEVEL;
}
AIFBasedModelBase::AterialInputFunctionType GetAterialInputFunction();
ModelBase::TimeGridType GetAterialInputFunctionTimeGrid();
protected:
AterialInputFunctionGenerator()
{
m_Mask = nullptr;
m_DynamicImage = nullptr;
this->SetDefaultHematocritLevel();
};
~AterialInputFunctionGenerator() override {};
//template <typename TPixel, unsigned int VDim>
//void DoCalculateAIF(itk::Image<TPixel, VDim>* image);
/** @brief Passes m_DynamicImage and m_Mask to the itkMaskedNaryStatisticsImageFilter and mitkExtractTimeGrid
* and inserts the result into m_AIFValues and m_AIFTimeGrid and modiefies the Timestamp*/
virtual void CalculateAIFAndGetResult();
/** @brief Makes sure that m_DynamicImage and m_Mask are set */
virtual void CheckValidInputs() const;
bool HasOutdatedResults();
itk::TimeStamp m_GenerationTimeStamp;
private:
Image::ConstPointer m_DynamicImage;
Image::ConstPointer m_Mask;
AIFBasedModelBase::AterialInputFunctionType m_AIFValues;
ModelBase::TimeGridType m_AIFTimeGrid;
double m_HCL;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h
index 1889ef0280..c052b4af86 100644
--- a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h
+++ b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h
@@ -1,167 +1,167 @@
/*============================================================================
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 mitkConcentrationCurveGenerator_h
#define mitkConcentrationCurveGenerator_h
#include <mitkImage.h>
#include <itkBinaryFunctorImageFilter.h>
#include "mitkConvertToConcentrationAbsoluteFunctor.h"
#include "mitkConvertToConcentrationRelativeFunctor.h"
#include "MitkPharmacokineticsExports.h"
namespace mitk {
/** \class ConcentrationCurveGenerator
* \brief Converts a given 4D mitk::Image with MR signal values into a 4D mitk::Image with corresponding contrast agent concentration values
*
* From a given 4D image, the Generator takes the 3D image of the first time point as baseline image. It then loops over all time steps, casts
* the current 3D image to itk and passes it to the ConvertToconcentrationFunctor. The returned 3D image has now values of concentration type and is stored at its timepoint
* in the return image.
*/
class MITKPHARMACOKINETICS_EXPORT ConcentrationCurveGenerator : public itk::Object
{
public:
mitkClassMacroItkParent(ConcentrationCurveGenerator, itk::Object);
itkNewMacro(Self);
//typedef itk::Image<double,3> ImageType;
typedef itk::Image<double,3> ConvertedImageType;
/** Getter and Setter for 4D mitk::Image*/
itkSetConstObjectMacro(DynamicImage,Image);
itkGetConstObjectMacro(DynamicImage,Image);
- /** Parameters Relevant for conversion Calculation; Have to be Set externally (Sequence Dependend)*/
+ /** Parameters Relevant for conversion Calculation; Have to be Set externally (Sequence Dependent)*/
itkSetMacro(RelaxationTime, double);
itkGetConstReferenceMacro(RelaxationTime, double);
itkSetMacro(Relaxivity, double);
itkGetConstReferenceMacro(Relaxivity, double);
itkSetMacro(RecoveryTime, double);
itkGetConstReferenceMacro(RecoveryTime, double);
itkSetMacro(RepetitionTime, double);
itkGetConstReferenceMacro(RepetitionTime, double);
itkSetMacro(FlipAngle, double);
itkGetConstReferenceMacro(FlipAngle, double);
itkSetMacro(FlipAnglePDW, double);
itkGetConstReferenceMacro(FlipAnglePDW, double);
itkSetMacro(Factor, double);
itkGetConstReferenceMacro(Factor, double);
/** Getter and Setter for PDW Map image*/
itkSetConstObjectMacro(PDWImage,Image);
itkGetConstObjectMacro(PDWImage,Image);
itkSetMacro(T2Factor, double);
itkGetConstReferenceMacro(T2Factor, double);
itkSetMacro(T2EchoTime, double);
itkGetConstReferenceMacro(T2EchoTime, double);
/** @brief Calls Convert and returns the 4D mitk::image in Concentration units*/
itkSetMacro(BaselineStartTimeStep, unsigned int);
itkGetConstReferenceMacro(BaselineStartTimeStep, unsigned int);
itkSetMacro(BaselineEndTimeStep, unsigned int);
itkGetConstReferenceMacro(BaselineEndTimeStep, unsigned int);
itkSetMacro(isTurboFlashSequence,bool);
itkGetConstReferenceMacro(isTurboFlashSequence,bool);
itkSetMacro(AbsoluteSignalEnhancement,bool);
itkGetConstReferenceMacro(AbsoluteSignalEnhancement,bool);
itkSetMacro(RelativeSignalEnhancement,bool);
itkGetConstReferenceMacro(RelativeSignalEnhancement,bool);
itkSetMacro(UsingT1Map,bool);
itkGetConstReferenceMacro(UsingT1Map,bool);
itkSetMacro(isT2weightedImage,bool);
itkGetConstReferenceMacro(isT2weightedImage,bool);
Image::Pointer GetConvertedImage();
protected:
ConcentrationCurveGenerator();
~ConcentrationCurveGenerator() override;
template<class TPixel_input, class TPixel_baseline>
mitk::Image::Pointer convertToConcentration(const itk::Image<TPixel_input, 3> *itkInputImage, const itk::Image<TPixel_baseline, 3> *itkBaselineImage);
/** Calls ConvertToconcentrationFunctor for passed 3D itk::image*/
mitk::Image::Pointer ConvertSignalToConcentrationCurve(const mitk::Image* inputImage, const mitk::Image* baselineImage);
/** @brief Takes the 3D image of the first timepoint to set as baseline image*/
void PrepareBaselineImage();
template<class TPixel>
void CalculateAverageBaselineImage(const itk::Image<TPixel,4> *itkBaselineImage);
/** @brief loops over all timepoints, casts the current timepoint 3D mitk::image to itk and passes it to ConvertSignalToConcentrationCurve */
virtual void Convert();
private:
Image::ConstPointer m_DynamicImage;
Image::ConstPointer m_BaselineImage;
Image::ConstPointer m_PDWImage;
Image::Pointer m_ConvertSignalToConcentrationCurve_OutputImage;
Image::Pointer m_ConvertedImage;
bool m_isT2weightedImage;
bool m_isTurboFlashSequence;
bool m_AbsoluteSignalEnhancement;
bool m_RelativeSignalEnhancement;
bool m_UsingT1Map;
double m_Factor;
//=Recovery Time
double m_RecoveryTime;
//=Repetition Time TR
double m_RepetitionTime;
//= pre-CA T1 time
double m_RelaxationTime;
//= contrast agent relaxivity
double m_Relaxivity;
double m_FlipAngle;
double m_FlipAnglePDW;
double m_T2Factor;
double m_T2EchoTime;
// The baseline image is averaged from the signal within time step range [m_BaselineStartTimeStep, m_BaselineEndTimeStep].
// m_BaselineStartTimeStep is the first time frame, that is included into the baseline averaging (starting with 0).
unsigned int m_BaselineStartTimeStep;
// m_BaselinStopTimeStep is the last time frame, that is included into the baseline averaging.
unsigned int m_BaselineEndTimeStep;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h b/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h
index 5bc53dfc27..b459cf9b06 100644
--- a/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h
+++ b/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h
@@ -1,154 +1,154 @@
/*============================================================================
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 mitkConvolutionHelper_h
#define mitkConvolutionHelper_h
#include "itkArray.h"
#include "mitkAIFBasedModelBase.h"
#include <iostream>
#include "MitkPharmacokineticsExports.h"
namespace mitk {
/** @namespace convolution
* @brief Helper for itk implementation of vnl fourier transformation
- * This namespace provides functions for the preperation of vnl_fft_1d, including a wrapper
+ * This namespace provides functions for the preparation of vnl_fft_1d, including a wrapper
* for wrapping the convolution kernel (turning it inside out) and a function for zeropadding
* to avoid convolution artefacts. */
namespace convolution {
/** Some typedefs concerning data structures needed for vnl_fft_1d, which has vnl_vector< vcl_complex< double > >
* as output typ of the forward transformation fwd_transform. Input is of type vnl_vector< vcl_complex< T > >
* but since itk::Array is derived from vnl_vector, this works as well*/
/** @brief Function that wraps the kernel */
inline itk::Array<double> wrap1d(itk::Array<double> kernel)
{
int dim = kernel.GetNumberOfElements();
itk::Array<double> wrappedKernel(dim);
wrappedKernel.fill(0.);
for(int i=0; i< dim; ++i)
{
wrappedKernel.SetElement(i, kernel.GetElement((i+(dim/2))%dim));
}
return wrappedKernel;
}
- /** @brief Fuction for zeropadding (adding zeros) of an Array/vnl_vector, so that is has size paddedDimensions
+ /** @brief Function for zeropadding (adding zeros) of an Array/vnl_vector, so that is has size paddedDimensions
* @param unpaddedSpectrum
* @param paddedDimension Dimensions that the Array should have after padding (convolution dimensions)
* \remark dim = Dimensions of padded image --> PaddedDimension
* \remark m dimensions of larger image
* \remark n dimensions of image to be padded --> InitialDimension*/
inline itk::Array<double> zeropadding1d(itk::Array<double> unpaddedSpectrum, int paddedDimension)
{
int initialDimension = unpaddedSpectrum.GetNumberOfElements();
itk::Array<double> paddedSpectrum(paddedDimension);
paddedSpectrum.fill(0.);
if(paddedDimension > initialDimension)
{
unsigned int padding = paddedDimension - initialDimension;
for(int i=0; i<initialDimension ;++i)
{
paddedSpectrum.SetElement(i+padding/2, unpaddedSpectrum.GetElement(i));
}
}
return paddedSpectrum;
}
/** @brief Follow up function after back transformation from fourier space bwd_transform.
* removes padding and scales (transformed values have to be divided by transformation dimensions) */
inline itk::Array<double> unpadAndScale(itk::Array<double> convolutionResult, int initialDimension)
{
int transformationDimension = convolutionResult.size();
unsigned int padding = transformationDimension - initialDimension;
itk::Array<double> scaledResult(initialDimension);
scaledResult.fill(0.0);
for(int i = 0; i<initialDimension; ++i)
{
double value = convolutionResult(i+padding/2) / transformationDimension;
scaledResult.SetElement(i,value);
}
return scaledResult;
}
- /** @brief Convinience function for preparing 2 array for convolution with each other.
+ /** @brief Convenience function for preparing 2 array for convolution with each other.
* Takes both arrays of type itk::Array, zeropadds them to the sum of their sizes and wraps
* the one specified as kernel. Returns them as vnl_vector<vcl_complex<double> >, ready to
* be entered in fwd_transform*/
inline void prepareConvolution(const itk::Array<double>& kernel, const itk::Array<double>& spectrum, itk::Array<double>& preparedKernel, itk::Array<double>& preparedSpectrum ){
int convolutionDimensions = kernel.GetSize() + spectrum.GetSize();
// itk::Array<double> paddedKernel = zeropadding1d(kernel,convolutionDimensions);
preparedKernel=zeropadding1d(kernel,convolutionDimensions);
preparedSpectrum = zeropadding1d(spectrum,convolutionDimensions);
// preparedKernel = wrap1d(paddedKernel);
}
}
inline itk::Array<double> convoluteAIFWithExponential(mitk::ModelBase::TimeGridType timeGrid, mitk::AIFBasedModelBase::AterialInputFunctionType aif, double lambda)
{
/** @brief Iterative Formula to Convolve aif(t) with an exponential Residuefunction R(t) = exp(lambda*t)
**/
typedef itk::Array<double> ConvolutionResultType;
ConvolutionResultType convolution(timeGrid.GetSize());
convolution.fill(0.0);
convolution(0) = 0;
for(unsigned int i = 0; i< (timeGrid.GetSize()-1); ++i)
{
double dt = timeGrid(i+1) - timeGrid(i);
double m = (aif(i+1) - aif(i))/dt;
double edt = exp(-lambda *dt);
convolution(i+1) =edt * convolution(i)
+ (aif(i) - m*timeGrid(i))/lambda * (1 - edt )
+ m/(lambda * lambda) * ((lambda * timeGrid(i+1) - 1) - edt*(lambda*timeGrid(i) -1));
}
return convolution;
}
inline itk::Array<double> convoluteAIFWithConstant(mitk::ModelBase::TimeGridType timeGrid, mitk::AIFBasedModelBase::AterialInputFunctionType aif, double constant)
{
/** @brief Iterative Formula to Convolve aif(t) with a constant value by linear interpolation of the Aif between sampling points
**/
typedef itk::Array<double> ConvolutionResultType;
ConvolutionResultType convolution(timeGrid.GetSize());
convolution.fill(0.0);
convolution(0) = 0;
for(unsigned int i = 0; i< (timeGrid.GetSize()-1); ++i)
{
double dt = timeGrid(i+1) - timeGrid(i);
double m = (aif(i+1) - aif(i))/dt;
convolution(i+1) = convolution(i) + constant * (aif(i)*dt + m*timeGrid(i)*dt + m/2*(timeGrid(i+1)*timeGrid(i+1) - timeGrid(i)*timeGrid(i)));
}
return convolution;
}
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h b/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h
index 20945815cb..28dcceee4e 100644
--- a/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h
+++ b/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h
@@ -1,76 +1,76 @@
/*============================================================================
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 mitkCurveDescriptionParameterBase_h
#define mitkCurveDescriptionParameterBase_h
#include <iostream>
#include <itkArray.h>
#include <itkArray2D.h>
#include <itkObject.h>
#include <mitkModelBase.h>
#include "MitkPharmacokineticsExports.h"
namespace mitk
{
/** Base class for functor that compute descriptive values for
a curve (e.g. like Area under the Curve, Time to peek, maximum,...)
@remark The derived classes must be implemented thread safe because GetCurveDescriptionParameter()
and GetDescriptionParameterName() of one instance may be called in
multi-threaded context (e.g. DescriptionParameterImageGeneratorBase
and derived classes). */
class MITKPHARMACOKINETICS_EXPORT CurveDescriptionParameterBase : public itk::Object
{
public:
typedef CurveDescriptionParameterBase Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(CurveDescriptionParameterBase, itk::Object);
typedef itk::Array<double> CurveType;
typedef itk::Array<double> CurveGridType;
typedef double CurveDescriptionParameterResultType;
typedef std::string CurveDescriptionParameterNameType;
typedef std::vector<CurveDescriptionParameterResultType> DescriptionParameterResultsType;
typedef std::vector<CurveDescriptionParameterNameType> DescriptionParameterNamesType;
/** Returns the concrete description values for a curve.
* @pre Curve value vector and curve grid must have the same size*/
DescriptionParameterResultsType GetCurveDescriptionParameter(const CurveType& curve, const CurveGridType& grid) const;
- /**Return the names of all descrition values that will be computed by the class.
+ /**Return the names of all description values that will be computed by the class.
* @post The order of names equales the order of the results of GetCurveDescriptionParameter().*/
virtual DescriptionParameterNamesType GetDescriptionParameterName() const = 0 ;
protected:
/** Slot to implement the computation of the descriptor values.*/
virtual DescriptionParameterResultsType ComputeCurveDescriptionParameter(const CurveType& curve, const CurveGridType& grid) const = 0;
CurveDescriptionParameterBase();
~CurveDescriptionParameterBase() override;
private:
//No copy constructor allowed
CurveDescriptionParameterBase(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h b/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h
index 9c91dc86c4..246f78f8e0 100644
--- a/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h
+++ b/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h
@@ -1,73 +1,73 @@
/*============================================================================
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 mitkCurveParameterFunctor_h
#define mitkCurveParameterFunctor_h
#include "mitkCurveDescriptionParameterBase.h"
#include "mitkSimpleFunctorBase.h"
#include "MitkPharmacokineticsExports.h"
namespace mitk
{
/**Functor for the curve description values by using the itkMulitOutputNaryImageFilter.
* You may register any number of CurveDescriptionParamterBase instances to the functor.
* The Functor will compute all values.
* @warning the functor must be threadsafe and so must be the registered CurveDescriptionParamterBase instances.*/
class MITKPHARMACOKINETICS_EXPORT CurveParameterFunctor : public SimpleFunctorBase
{
public:
typedef CurveParameterFunctor Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkFactorylessNewMacro(Self);
itkTypeMacro(CurveParameterFunctor, SimpleFunctorBase);
typedef CurveDescriptionParameterBase::CurveDescriptionParameterNameType ParameterNameType;
typedef CurveDescriptionParameterBase::DescriptionParameterNamesType ParameterNamesType;
using GridArrayType = SimpleFunctorBase::GridArrayType;
SimpleFunctorBase::OutputPixelVectorType Compute(const InputPixelVectorType & value) const override;
unsigned int GetNumberOfOutputs() const override;
GridArrayType GetGrid() const override;
itkSetMacro(Grid, GridArrayType);
ParameterNamesType GetDescriptionParameterNames() const;
- /**@warning Teh function is currently not thread safe.
+ /**@warning The function is currently not thread safe.
@todo reimplement with shareable lock to allow other class methods to be used parallel but lock this one exclusively.*/
void ResetDescriptionParameters();
- /**@warning Teh function is currently not thread safe.
+ /**@warning The function is currently not thread safe.
@todo reimplement with shareable lock to allow other class methods to be used parallel but lock this one exclusively.*/
void RegisterDescriptionParameter(const ParameterNameType& parameterName, CurveDescriptionParameterBase* parameterFunction);
- /**@warning Teh function is currently not thread safe.
+ /**@warning The function is currently not thread safe.
@todo reimplement with shareable lock to allow other class methods to be used parallel but lock this one exclusively.*/
const CurveDescriptionParameterBase* GetDescriptionParameterFunction(const ParameterNameType& parameterName) const;
protected:
CurveParameterFunctor();
~CurveParameterFunctor() override;
private:
typedef std::map<ParameterNameType, CurveDescriptionParameterBase::Pointer> DescriptionParameterMapType;
DescriptionParameterMapType m_DescriptorMap;
GridArrayType m_Grid;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h b/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h
index a74a6aa15f..956a75ced3 100644
--- a/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h
+++ b/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h
@@ -1,92 +1,92 @@
/*============================================================================
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 mitkDescriptivePharmacokineticBrixModelParameterizer_h
#define mitkDescriptivePharmacokineticBrixModelParameterizer_h
#include "mitkConcreteModelParameterizerBase.h"
#include "mitkDescriptivePharmacokineticBrixModel.h"
namespace mitk
{
/** Parameterizer for the DescriptivePharmacokineticBrixModel that use an image
for initializing the model. This parameterizer is amongst others used for pixel based fiting
strategies.
@sa DescriptivePharmacokineticBrixModelParameterizer*/
class MITKPHARMACOKINETICS_EXPORT DescriptivePharmacokineticBrixModelParameterizer : public
ConcreteModelParameterizerBase<mitk::DescriptivePharmacokineticBrixModel>
{
public:
typedef DescriptivePharmacokineticBrixModelParameterizer Self;
typedef ConcreteModelParameterizerBase<mitk::DescriptivePharmacokineticBrixModel> Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(DescriptivePharmacokineticBrixModelParameterizer, ConcreteModelParameterizerBase);
itkNewMacro(Self);
typedef Superclass::ModelBaseType ModelBaseType;
typedef Superclass::ModelBasePointer ModelBasePointer;
typedef Superclass::ModelType ModelType;
typedef Superclass::ModelPointer ModelPointer;
typedef Superclass::StaticParameterValueType StaticParameterValueType;
typedef Superclass::StaticParameterValuesType StaticParameterValuesType;
typedef Superclass::StaticParameterMapType StaticParameterMapType;
typedef itk::Image<double, 3> BaseImageType;
typedef Superclass::IndexType IndexType;
itkSetMacro(Tau, double);
itkGetConstReferenceMacro(Tau, double);
itkSetConstObjectMacro(BaseImage, BaseImageType);
itkGetConstObjectMacro(BaseImage, BaseImageType);
/* Returns the global static parameters for the model.
* @remark this default implementation assumes no global static parameters exist.
* Thus an empty map is returned.*/
StaticParameterMapType GetGlobalStaticParameters() const override;
/* Returns the local static parameters for the model at the given index.
* @remark this default implementation assumes no local static parameters exist.
* Thus an empty map is returned.*/
StaticParameterMapType GetLocalStaticParameters(const IndexType& currentPosition) const override;
/** This function returns the default parameterization (e.g. initial parametrization for fitting)
defined by the model developer for for the given model.*/
ParametersType GetDefaultInitialParameterization() const override;
protected:
DescriptivePharmacokineticBrixModelParameterizer();
~DescriptivePharmacokineticBrixModelParameterizer() override;
/**injection time Tau in minutes [min]*/
double m_Tau;
- /**Pointer to the image that containes the values of the first timestep
+ /**Pointer to the image that contains the values of the first timestep
(base value of the series that should be modelled)*/
BaseImageType::ConstPointer m_BaseImage;
private:
//No copy constructor allowed
DescriptivePharmacokineticBrixModelParameterizer(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp b/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp
index 14be13fc5b..8a43391814 100644
--- a/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp
+++ b/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp
@@ -1,90 +1,90 @@
/*============================================================================
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 <mitkImageTimeSelector.h>
#include <mitkImageCast.h>
#include "mitkDescriptivePharmacokineticBrixModelFactory.h"
#include "mitkDescriptivePharmacokineticBrixModelParameterizer.h"
#include "mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h"
mitk::DescriptivePharmacokineticBrixModelFactory::DescriptivePharmacokineticBrixModelFactory()
{
};
mitk::DescriptivePharmacokineticBrixModelFactory::~DescriptivePharmacokineticBrixModelFactory()
{
};
mitk::ModelParameterizerBase::ParametersType
mitk::DescriptivePharmacokineticBrixModelFactory::GetDefaultInitialParameterization() const
{
return DescriptivePharmacokineticBrixModelParameterizer::New()->GetDefaultInitialParameterization();
};
mitk::ModelParameterizerBase::Pointer
mitk::DescriptivePharmacokineticBrixModelFactory::DoCreateParameterizer(
const mitk::modelFit::ModelFitInfo* fit) const
{
mitk::ModelParameterizerBase::Pointer result;
if (fit->fitType == ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED())
{
DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer =
DescriptivePharmacokineticBrixModelParameterizer::New();
modelFit::StaticParameterMap::ValueType tau = fit->staticParamMap.Get(
DescriptivePharmacokineticBrixModel::NAME_STATIC_PARAMETER_tau);
modelParameterizer->SetTau(tau[0]);
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(fit->inputImage);
imageTimeSelector->SetTimeNr(0);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage;
mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage);
modelParameterizer->SetBaseImage(baseImage);
result = modelParameterizer.GetPointer();
}
else if (fit->fitType == ModelFitConstants::FIT_TYPE_VALUE_ROIBASED())
{
DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer =
DescriptivePharmacokineticBrixModelValueBasedParameterizer::New();
modelFit::StaticParameterMap::ValueType tau = fit->staticParamMap.Get(
DescriptivePharmacokineticBrixModel::NAME_STATIC_PARAMETER_tau);
modelParameterizer->SetTau(tau[0]);
if (!fit->inputData.ValueExists("ROI"))
{
mitkThrow() <<
- "Cannot generate parameterizer for fit of type ROIbased. Input data with the lable \"ROI\" is missing in fit.";
+ "Cannot generate parameterizer for fit of type ROIbased. Input data with the label \"ROI\" is missing in fit.";
}
ScalarListLookupTable::ValueType signal = fit->inputData.GetTableValue("ROI");
if (signal.empty())
{
mitkThrow() <<
- "Cannot generate parameterizer for fit of type ROIbased. Input data with the lable \"ROI\" is invalid: No values available.";
+ "Cannot generate parameterizer for fit of type ROIbased. Input data with the label \"ROI\" is invalid: No values available.";
}
modelParameterizer->SetBaseValue(signal[0]);
result = modelParameterizer.GetPointer();
}
return result;
};
diff --git a/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp b/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp
index bd0a16a8d3..3f73a89946 100644
--- a/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp
+++ b/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp
@@ -1,123 +1,123 @@
/*============================================================================
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 "QmitkDescriptionParameterBackgroundJob.h"
#include "mitkModelFitInfo.h"
void DescriptionParameterBackgroundJob::OnComputeEvent(::itk::Object* caller,
const itk::EventObject& event)
{
itk::ProgressEvent progressEvent;
itk::InitializeEvent initEvent;
itk::StartEvent startEvent;
itk::EndEvent endEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::DescriptionParameterImageGeneratorBase* castedReporter =
dynamic_cast<mitk::DescriptionParameterImageGeneratorBase*>(caller);
emit JobProgress(castedReporter->GetProgress());
}
else if (initEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Initializing description parameter generator"));
}
else if (startEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Started parameter computation process."));
}
else if (endEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Finished parameter computation process."));
}
}
DescriptionParameterBackgroundJob::
DescriptionParameterBackgroundJob(mitk::DescriptionParameterImageGeneratorBase* generator,
mitk::DataNode* parentNode)
{
if (!generator)
{
mitkThrow() << "Cannot create description parameter background job. Passed fit generator is NULL.";
}
m_Generator = generator;
m_ParentNode = parentNode;
m_spCommand = ::itk::MemberCommand<DescriptionParameterBackgroundJob>::New();
m_spCommand->SetCallbackFunction(this, &DescriptionParameterBackgroundJob::OnComputeEvent);
m_ObserverID = m_Generator->AddObserver(::itk::AnyEvent(), m_spCommand);
};
mitk::DataNode*
DescriptionParameterBackgroundJob::
GetParentNode() const
{
return m_ParentNode;
};
DescriptionParameterBackgroundJob::
~DescriptionParameterBackgroundJob()
{
m_Generator->RemoveObserver(m_ObserverID);
};
mitk::modelFit::ModelFitResultNodeVectorType DescriptionParameterBackgroundJob::CreateResultNodes(
const mitk::DescriptionParameterImageGeneratorBase::ParameterImageMapType& paramimages)
{
mitk::modelFit::ModelFitResultNodeVectorType results;
for (const auto &image : paramimages)
{
if (image.second.IsNull())
{
mitkThrow() << "Cannot generate result node. Passed parameterImage is null. parameter name: " <<
image.first;
}
mitk::DataNode::Pointer result = mitk::DataNode::New();
result->SetData(image.second);
result->SetName(image.first);
result->SetVisibility(true);
results.push_back(result);
}
return results;
};
void
DescriptionParameterBackgroundJob::
run()
{
try
{
emit JobStatusChanged(QString("Started session..."));
m_Generator->Generate();
emit JobStatusChanged(QString("Generate result nodes."));
m_Results = CreateResultNodes(m_Generator->GetParameterImages());
emit ResultsAreAvailable(m_Results, this);
}
catch (::std::exception& e)
{
emit Error(QString("Error while processing data. Details: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when processing the data."));
+ emit Error(QString("Unknown error when processing the data."));
}
emit Finished();
};
diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp
index 8407bb074e..5f6a7ba2c5 100644
--- a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp
+++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp
@@ -1,562 +1,562 @@
/*============================================================================
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 <mitkPlanarFigureIO.h>
#include "mitkCustomMimeType.h"
#include "mitkIOMimeTypes.h"
#include "mitkExceptionMacro.h"
#include "mitkPlanarAngle.h"
#include "mitkPlanarArrow.h"
#include "mitkPlanarBezierCurve.h"
#include "mitkPlanarCircle.h"
#include "mitkPlanarCross.h"
#include "mitkPlanarDoubleEllipse.h"
#include "mitkPlanarEllipse.h"
#include "mitkPlanarFourPointAngle.h"
#include "mitkPlanarLine.h"
#include "mitkPlanarPolygon.h"
#include "mitkPlanarRectangle.h"
#include "mitkPlanarSubdivisionPolygon.h"
#include "mitkPlaneGeometry.h"
#include "mitkBasePropertySerializer.h"
#include <mitkLocaleSwitch.h>
#include <tinyxml2.h>
namespace mitk
{
PlanarFigureIO::PlanarFigureIO()
: AbstractFileIO(PlanarFigure::GetStaticNameOfClass())
{
std::string category = "MITK PlanarFigure File";
CustomMimeType customMimeType;
customMimeType.SetCategory(category);
customMimeType.AddExtension("pf");
this->AbstractFileIOWriter::SetMimeType(customMimeType);
this->AbstractFileIOWriter::SetDescription(category);
customMimeType.AddExtension("pf");
customMimeType.AddExtension("PF");
this->AbstractFileIOReader::SetMimeType(customMimeType);
this->AbstractFileIOReader::SetDescription(category);
AbstractFileWriter::SetRanking(10);
AbstractFileReader::SetRanking(10);
this->RegisterService();
}
IFileIO::ConfidenceLevel PlanarFigureIO::GetWriterConfidenceLevel() const
{
if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
return Unsupported;
const auto *input = static_cast<const PlanarFigure *>(this->GetInput());
if (input != nullptr)
return Supported;
else
return Unsupported;
}
void PlanarFigureIO::Write()
{
this->ValidateOutputLocation();
mitk::LocaleSwitch localeSwitch("C");
tinyxml2::XMLDocument document;
document.InsertEndChild(document.NewDeclaration());
auto *version = document.NewElement("Version");
version->SetAttribute("Writer", __FILE__);
version->SetAttribute("CVSRevision", "$Revision: 17055 $");
version->SetAttribute("FileVersion", 1);
document.InsertEndChild(version);
auto pf = dynamic_cast<const PlanarFigure*>(this->GetInput());
if (pf == nullptr)
{
mitkThrow() << "Try to safe a BaseData instance as PlanarFigure. That is not a planar figure. This should not happen and is a violated precondition. Please check the program logic.";
}
auto *pfElement = document.NewElement("PlanarFigure");
pfElement->SetAttribute("type", pf->GetNameOfClass());
document.InsertEndChild(pfElement);
// Serialize property list of PlanarFigure
mitk::PropertyList::Pointer propertyList = pf->GetPropertyList();
mitk::PropertyList::PropertyMap::const_iterator it;
for (it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); ++it)
{
- // Create seralizer for this property
+ // Create serializer for this property
const mitk::BaseProperty* prop = it->second;
std::string serializerName = std::string(prop->GetNameOfClass()) + "Serializer";
std::list<itk::LightObject::Pointer> allSerializers =
itk::ObjectFactoryBase::CreateAllInstance(serializerName.c_str());
if (allSerializers.size() != 1)
{
// No or too many serializer(s) found, skip this property
continue;
}
auto* serializer =
dynamic_cast<mitk::BasePropertySerializer*>(allSerializers.begin()->GetPointer());
if (serializer == nullptr)
{
// Serializer not valid; skip this property
}
auto *keyElement = document.NewElement("property");
keyElement->SetAttribute("key", it->first.c_str());
keyElement->SetAttribute("type", prop->GetNameOfClass());
serializer->SetProperty(prop);
tinyxml2::XMLElement* valueElement = nullptr;
try
{
valueElement = serializer->Serialize(document);
}
catch (...)
{
}
if (valueElement == nullptr)
{
// Serialization failed; skip this property
continue;
}
// Add value to property element
keyElement->InsertEndChild(valueElement);
// Append serialized property to property list
pfElement->InsertEndChild(keyElement);
}
// Serialize control points of PlanarFigure
auto *controlPointsElement = document.NewElement("ControlPoints");
pfElement->InsertEndChild(controlPointsElement);
for (unsigned int i = 0; i < pf->GetNumberOfControlPoints(); i++)
{
auto *vElement = document.NewElement("Vertex");
vElement->SetAttribute("id", i);
vElement->SetAttribute("x", pf->GetControlPoint(i)[0]);
vElement->SetAttribute("y", pf->GetControlPoint(i)[1]);
controlPointsElement->InsertEndChild(vElement);
}
auto *geoElement = document.NewElement("Geometry");
const auto* planeGeo = dynamic_cast<const PlaneGeometry*>(pf->GetPlaneGeometry());
if (planeGeo != nullptr)
{
// Write parameters of IndexToWorldTransform of the PlaneGeometry
typedef mitk::Geometry3D::TransformType TransformType;
const TransformType* affineGeometry = planeGeo->GetIndexToWorldTransform();
const TransformType::ParametersType& parameters = affineGeometry->GetParameters();
auto *vElement = document.NewElement("transformParam");
for (unsigned int i = 0; i < affineGeometry->GetNumberOfParameters(); ++i)
{
std::stringstream paramName;
paramName << "param" << i;
vElement->SetAttribute(paramName.str().c_str(), parameters.GetElement(i));
}
geoElement->InsertEndChild(vElement);
// Write bounds of the PlaneGeometry
typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType;
const BoundsArrayType& bounds = planeGeo->GetBounds();
vElement = document.NewElement("boundsParam");
for (unsigned int i = 0; i < 6; ++i)
{
std::stringstream boundName;
boundName << "bound" << i;
vElement->SetAttribute(boundName.str().c_str(), bounds.GetElement(i));
}
geoElement->InsertEndChild(vElement);
// Write spacing and origin of the PlaneGeometry
Vector3D spacing = planeGeo->GetSpacing();
Point3D origin = planeGeo->GetOrigin();
geoElement->InsertEndChild(this->CreateXMLVectorElement(document, "Spacing", spacing));
geoElement->InsertEndChild(this->CreateXMLVectorElement(document, "Origin", origin));
pfElement->InsertEndChild(geoElement);
}
if (this->GetOutputStream() != nullptr)
{
tinyxml2::XMLPrinter printer;
document.Print(&printer);
*(this->GetOutputStream()) << printer.CStr();
}
else
{
if (document.SaveFile(this->GetOutputLocation().c_str()) != tinyxml2::XML_SUCCESS)
{
MITK_ERROR << "Could not write planar figures to " << this->GetOutputLocation() << "\nTinyXML reports '" << document.ErrorStr()
<< "'";
throw std::ios_base::failure("Error during writing of planar figure xml file.");
}
}
}
tinyxml2::XMLElement* mitk::PlanarFigureIO::CreateXMLVectorElement(tinyxml2::XMLDocument& doc, const char* name, itk::FixedArray<mitk::ScalarType, 3> v)
{
auto vElement = doc.NewElement(name);
vElement->SetAttribute("x", v.GetElement(0));
vElement->SetAttribute("y", v.GetElement(1));
vElement->SetAttribute("z", v.GetElement(2));
return vElement;
}
IFileIO::ConfidenceLevel PlanarFigureIO::GetReaderConfidenceLevel() const
{
if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
return Unsupported;
return Supported;
//Remark: The original reader code assumed that al pf files can be read.
//So no content checking was done. Thus was not implemented while refactoring
//to services yet. But I think it would make sense.
}
std::vector<BaseData::Pointer> PlanarFigureIO::DoRead()
{
mitk::LocaleSwitch localeSwitch("C");
std::vector<BaseData::Pointer> results;
tinyxml2::XMLDocument document;
if (this->GetInputStream() != nullptr)
{
std::string s(std::istreambuf_iterator<char>(*(this->GetInputStream())), {});
document.Parse(s.c_str());
//Remark: didn't use *(this->GetInputStream()) >> document;
//because our PlanarFigure files version 1 are illformed (multiple top level elements)
- //and therefor tinyxml does not read them completly when streamed directly.
+ //and therefor tinyxml does not read them completely when streamed directly.
//only the first (version element) is read.
}
else
{
if (tinyxml2::XML_SUCCESS != document.LoadFile(this->GetInputLocation().c_str()))
{
MITK_ERROR << "Could not open/read/parse " << this->GetInputLocation() << ". TinyXML reports: '" << document.ErrorStr() << "'.";
return {};
}
}
int fileVersion = 1;
auto* versionObject = document.FirstChildElement("Version");
if (versionObject != nullptr)
{
if (versionObject->QueryIntAttribute("FileVersion", &fileVersion) != tinyxml2::XML_SUCCESS)
{
MITK_WARN << this->GetInputLocation() << " does not contain version information! Trying version 1 format." << std::endl;
}
}
else
{
MITK_WARN << this->GetInputLocation() << " does not contain version information! Trying version 1 format." << std::endl;
}
if (fileVersion !=
1) // add file version selection and version specific file parsing here, if newer file versions are created
{
MITK_WARN << "File version > 1 is not supported by this reader.";
return {};
}
/* file version 1 reader code */
for (auto* pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr;
pfElement = pfElement->NextSiblingElement("PlanarFigure"))
{
const char* typeC = pfElement->Attribute("type");
std::string type = nullptr != typeC
? typeC
: "";
mitk::PlanarFigure::Pointer planarFigure = nullptr;
if (type == "PlanarAngle")
{
planarFigure = mitk::PlanarAngle::New();
}
else if (type == "PlanarCircle")
{
planarFigure = mitk::PlanarCircle::New();
}
else if (type == "PlanarEllipse")
{
planarFigure = mitk::PlanarEllipse::New();
}
else if (type == "PlanarCross")
{
planarFigure = mitk::PlanarCross::New();
}
else if (type == "PlanarFourPointAngle")
{
planarFigure = mitk::PlanarFourPointAngle::New();
}
else if (type == "PlanarLine")
{
planarFigure = mitk::PlanarLine::New();
}
else if (type == "PlanarPolygon")
{
planarFigure = mitk::PlanarPolygon::New();
}
else if (type == "PlanarSubdivisionPolygon")
{
planarFigure = mitk::PlanarSubdivisionPolygon::New();
}
else if (type == "PlanarRectangle")
{
planarFigure = mitk::PlanarRectangle::New();
}
else if (type == "PlanarArrow")
{
planarFigure = mitk::PlanarArrow::New();
}
else if (type == "PlanarDoubleEllipse")
{
planarFigure = mitk::PlanarDoubleEllipse::New();
}
else if (type == "PlanarBezierCurve")
{
planarFigure = mitk::PlanarBezierCurve::New();
}
else
{
// unknown type
MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element.";
continue;
}
// Read properties of the planar figure
for (auto* propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr;
propertyElement = propertyElement->NextSiblingElement("property"))
{
const char* keya = propertyElement->Attribute("key");
const std::string key(keya ? keya : "");
const char* typea = propertyElement->Attribute("type");
const std::string type(typea ? typea : "");
// hand propertyElement to specific reader
std::stringstream propertyDeserializerClassName;
propertyDeserializerClassName << type << "Serializer";
const std::list<itk::LightObject::Pointer> readers =
itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str());
if (readers.size() < 1)
{
MITK_ERROR << "No property reader found for " << type;
}
if (readers.size() > 1)
{
MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one.";
}
for (auto iter = readers.cbegin(); iter != readers.cend(); ++iter)
{
if (auto* reader = dynamic_cast<BasePropertySerializer*>(iter->GetPointer()))
{
const BaseProperty::Pointer property = reader->Deserialize(propertyElement->FirstChildElement());
if (property.IsNotNull())
{
planarFigure->GetPropertyList()->ReplaceProperty(key, property);
}
else
{
MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type
<< ". Your data may be corrupted";
}
break;
}
}
}
// If we load a planarFigure, it has definitely been placed correctly.
// If we do not set this property here, we cannot load old planarFigures
// without messing up the interaction (PF-Interactor needs this property.
planarFigure->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
// Which features (length or circumference etc) a figure has is decided by whether it is closed or not
// the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature
auto* planarPolygon = dynamic_cast<PlanarPolygon*>(planarFigure.GetPointer());
if (planarPolygon != nullptr)
{
bool isClosed = false;
planarFigure->GetPropertyList()->GetBoolProperty("closed", isClosed);
planarPolygon->SetClosed(isClosed);
}
// Read geometry of containing plane
auto* geoElement = pfElement->FirstChildElement("Geometry");
if (geoElement != nullptr)
{
try
{
// Create plane geometry
mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New();
// Extract and set plane transform parameters
const DoubleList transformList =
this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("transformParam"), "param", 12);
typedef mitk::BaseGeometry::TransformType TransformType;
TransformType::ParametersType parameters;
parameters.SetSize(12);
unsigned int i;
DoubleList::const_iterator it;
for (it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i)
{
parameters.SetElement(i, *it);
}
typedef mitk::BaseGeometry::TransformType TransformType;
TransformType::Pointer affineGeometry = TransformType::New();
affineGeometry->SetParameters(parameters);
planeGeo->SetIndexToWorldTransform(affineGeometry);
// Extract and set plane bounds
const DoubleList boundsList =
this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("boundsParam"), "bound", 6);
typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType;
BoundsArrayType bounds;
for (it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i)
{
bounds[i] = *it;
}
planeGeo->SetBounds(bounds);
// Extract and set spacing and origin
const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing"));
planeGeo->SetSpacing(spacing);
const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin"));
planeGeo->SetOrigin(origin);
planarFigure->SetPlaneGeometry(planeGeo);
}
catch (...)
{
}
}
auto* cpElement = pfElement->FirstChildElement("ControlPoints");
bool first = true;
if (cpElement != nullptr)
for (auto* vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr;
vertElement = vertElement->NextSiblingElement("Vertex"))
{
int id = 0;
mitk::Point2D::ValueType x = 0.0;
mitk::Point2D::ValueType y = 0.0;
if (vertElement->QueryIntAttribute("id", &id) != tinyxml2::XML_SUCCESS)
return{}; // TODO: can we do a better error handling?
if (vertElement->QueryDoubleAttribute("x", &x) != tinyxml2::XML_SUCCESS)
return{}; // TODO: can we do a better error handling?
if (vertElement->QueryDoubleAttribute("y", &y) != tinyxml2::XML_SUCCESS)
return{}; // TODO: can we do a better error handling?
Point2D p;
p.SetElement(0, x);
p.SetElement(1, y);
if (first == true) // needed to set m_FigurePlaced to true
{
planarFigure->PlaceFigure(p);
first = false;
}
planarFigure->SetControlPoint(id, p, true);
}
// Calculate feature quantities of this PlanarFigure
planarFigure->EvaluateFeatures();
// Make sure that no control point is currently selected
planarFigure->DeselectControlPoint();
if (planarFigure.IsNotNull())
{
results.emplace_back(planarFigure);
}
}
return results;
}
mitk::PlanarFigureIO::DoubleList mitk::PlanarFigureIO::GetDoubleAttributeListFromXMLNode(
const tinyxml2::XMLElement* e, const char* attributeNameBase, unsigned int count)
{
DoubleList list;
if (e == nullptr)
throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
for (unsigned int i = 0; i < count; ++i)
{
mitk::ScalarType p(-1.0);
std::stringstream attributeName;
attributeName << attributeNameBase << i;
if (e->QueryDoubleAttribute(attributeName.str().c_str(), &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
list.push_back(p);
}
return list;
}
mitk::Point3D mitk::PlanarFigureIO::GetPointFromXMLNode(const tinyxml2::XMLElement* e)
{
if (e == nullptr)
throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
mitk::Point3D point;
mitk::ScalarType p(-1.0);
if (e->QueryDoubleAttribute("x", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
point.SetElement(0, p);
if (e->QueryDoubleAttribute("y", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
point.SetElement(1, p);
if (e->QueryDoubleAttribute("z", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
point.SetElement(2, p);
return point;
}
mitk::Vector3D mitk::PlanarFigureIO::GetVectorFromXMLNode(const tinyxml2::XMLElement* e)
{
if (e == nullptr)
throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
mitk::Vector3D vector;
mitk::ScalarType p(-1.0);
if (e->QueryDoubleAttribute("x", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
vector.SetElement(0, p);
if (e->QueryDoubleAttribute("y", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
vector.SetElement(1, p);
if (e->QueryDoubleAttribute("z", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
vector.SetElement(2, p);
return vector;
}
PlanarFigureIO *PlanarFigureIO::IOClone() const { return new PlanarFigureIO(*this); }
} // namespace
diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h
index 0c7c08f292..395f05b730 100644
--- a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h
+++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h
@@ -1,97 +1,97 @@
/*============================================================================
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 mitkPlanarFigureIO_h
#define mitkPlanarFigureIO_h
#include <mitkAbstractFileIO.h>
#include <mitkPlanarFigure.h>
namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
namespace mitk
{
/**
* Reads/Writes a PlanarFigure to a file
* @ingroup Process
*/
class PlanarFigureIO : public mitk::AbstractFileIO
{
public:
typedef mitk::PlanarFigure InputType;
PlanarFigureIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
protected:
/**
* @brief Reads a number of mitk::PlanarFigures from the file system
* @return a vector of mitk::PlanarFigures
- * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
+ * @throws throws an mitk::Exception if an error occurs during parsing the nrrd header
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
using DoubleList = std::list<double>;
/**
* \brief parses the element for the attributes name0 to nameN, where "name" and the number of attributes
* to read are passed as argument. Returns a list of double vales.
* \param[in] e the XML element that will be parsed
* \param[in] attributeNameBase the basic name of the parameters
* \param[in] count the number of parameters
* \return returns a mitk::Point3D with the values x,y,z
*/
DoubleList GetDoubleAttributeListFromXMLNode(const tinyxml2::XMLElement* e, const char* attributeNameBase, unsigned int count);
/**
* \brief parses the element for the attributes x,y,z and returns a mitk::Vector3D filled with these values
* \param[in] e the XML element that will be parsed
* \return returns a mitk::Vector3D with the values x,y,z
*/
static mitk::Vector3D GetVectorFromXMLNode(const tinyxml2::XMLElement* e);
/**
* \brief parses the element for the attributes x,y,z and returns a mitk::Point3D filled with these values
* \param[in] e the XML element that will be parsed
* \return returns a mitk::Point3D with the values x,y,z
*/
static mitk::Point3D GetPointFromXMLNode(const tinyxml2::XMLElement* e);
/**Documentation
* \brief creates a TinyXML element that contains x, y, and z values
*
* \param[in] doc
* \param[in] name the name of the XML element
* \param[in] v the vector or point that contains the x, y and z values
* \return returns a XML element named name and three attributes x, y and z.
*/
static tinyxml2::XMLElement* CreateXMLVectorElement(tinyxml2::XMLDocument& doc, const char* name, itk::FixedArray<mitk::ScalarType, 3> v);
private:
PlanarFigureIO *IOClone() const override;
};
} // end of namespace mitk
#endif
diff --git a/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h b/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h
index a3cd22f028..fb4e62a52a 100644
--- a/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h
+++ b/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkImageToPlanarFigureFilter_h
#define mitkImageToPlanarFigureFilter_h
#include "mitkCommon.h"
#include "mitkImage.h"
#include "mitkPlanarFigureSource.h"
namespace mitk
{
//##Documentation
//## @brief Superclass of all classes having one or more Images as input and
//## generating PlanarFigures as output
//## @ingroup MitkPlanarFigureModule
class MITKPLANARFIGURE_EXPORT ImageToPlanarFigureFilter : public PlanarFigureSource
{
public:
mitkClassMacro(ImageToPlanarFigureFilter, PlanarFigureSource);
// itkFactorylessNewMacro(Self)
// itkCloneMacro(Self)
/** Some convenient typedefs. */
typedef mitk::Image InputImageType;
typedef InputImageType::Pointer InputImagePointer;
typedef InputImageType::ConstPointer InputImageConstPointer;
typedef SlicedData::RegionType InputImageRegionType;
/** Set/Get the image input of this process object. */
using Superclass::SetInput;
virtual void SetInput(const InputImageType *image);
virtual void SetInput(unsigned int, const InputImageType *image);
const InputImageType *GetInput(void);
const InputImageType *GetInput(unsigned int idx);
protected:
ImageToPlanarFigureFilter();
~ImageToPlanarFigureFilter() override;
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
/** What is the input requested region that is required to produce the
* output requested region? The base assumption for image processing
* filters is that the input requested region can be set to match the
* output requested region. If a filter requires more input (for instance
* a filter that uses neighborhoods needs more input than output to avoid
* introducing artificial boundary conditions) or less input (for instance
* a magnify filter) will have to override this method. In doing so, it
* should call its superclass' implementation as its first step. Note that
* this imaging filters operate differently than the classes to this
- * point in the class hierachy. Up till now, the base assumption has been
+ * point in the class hierarchy. Up till now, the base assumption has been
* that the largest possible region will be requested of the input.
*
* \sa ProcessObject::GenerateInputRequestedRegion(),
* ImageSource::GenerateInputRequestedRegion() */
void GenerateInputRequestedRegion() override;
private:
void operator=(const Self &); // purposely not implemented
};
} // namespace mitk
#endif
diff --git a/Modules/PlanarFigure/include/mitkPlanarFigure.h b/Modules/PlanarFigure/include/mitkPlanarFigure.h
index d4ba5a9ca8..a5f5c355db 100644
--- a/Modules/PlanarFigure/include/mitkPlanarFigure.h
+++ b/Modules/PlanarFigure/include/mitkPlanarFigure.h
@@ -1,377 +1,377 @@
/*============================================================================
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 mitkPlanarFigure_h
#define mitkPlanarFigure_h
#include "mitkBaseData.h"
#include "mitkCommon.h"
#include <MitkPlanarFigureExports.h>
#include <deque>
namespace mitk
{
class PlaneGeometry;
/**
* \brief Base-class for geometric planar (2D) figures, such as
* lines, circles, rectangles, polygons, etc.
*
* \warning Currently does not support time-resolved data handling
*
* Behavior and appearance of PlanarFigures are controlled by various properties; for a detailed
* list of appearance properties see mitk::PlanarFigureMapper2D
*
* The following properties control general PlanarFigure behavior:
*
* <ul>
* <li>"selected": true if the planar figure is selected
* <li>"planarfigure.ishovering": true if the mouse "hovers" over the planar figure
* <li>"planarfigure.iseditable": true if the planar figure can be edited (otherwise,
* it can only be picked/selected, but its control points cannot be edited); default is true
* <li>"planarfigure.isextendable": true if new control points can be inserted into the list of control points;
* default is false
* </ul>
*
*
* TODO: Implement local 2D transform (including center of rotation...)
*
*/
class MITKPLANARFIGURE_EXPORT PlanarFigure : public BaseData
{
public:
mitkClassMacro(PlanarFigure, BaseData);
itkCloneMacro(Self);
typedef Point2D PolyLineElement;
typedef itk::VectorContainer<unsigned long, bool> BoolContainerType;
typedef std::deque<Point2D> ControlPointListType;
typedef std::vector<PolyLineElement> PolyLineType;
/** \brief Sets the 2D geometry on which this figure will be placed.
*
* In most cases, this is a Geometry already owned by another object, e.g.
* describing the slice of the image on which measurements will be
* performed.
*/
virtual void SetPlaneGeometry(mitk::PlaneGeometry *geometry);
/** \brief Returns (previously set) 2D geometry of this figure. */
virtual const PlaneGeometry *GetPlaneGeometry() const;
/** \brief True if the planar figure is closed.
*
* Default is false. The "closed" boolean property must be set in sub-classes. */
virtual bool IsClosed() const;
/** \brief True if the planar figure has been placed (and can be
* displayed/interacted with). */
virtual bool IsPlaced() const { return m_FigurePlaced; };
/** \brief Place figure at the given point (in 2D index coordinates) onto
* the given 2D geometry.
*
* By default, the first two control points of the figure are set to the
* passed point. Further points can be set via AddControlPoint(), if the
* current number of control points is below the maximum number of control
* points.
*
* Can be re-implemented in sub-classes as needed.
*/
virtual void PlaceFigure(const Point2D &point);
/**
* \brief Adds / inserts new control-points
*
* This method adds a new control-point with the coordinates defined by point at the given index.
* If 'index' == -1 or index is greater than the number of control-points the new point is appended
* to the back of the list of control points.
* If a control-point already exists for 'index', an additional point is inserted at that position.
* It is not possible to add more points if the maximum number of control-points (GetMaximumNumberOfControlPoints())
* has been reached.
*/
virtual bool AddControlPoint(const Point2D &point, int index = -1);
virtual bool SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist = false);
virtual bool SetCurrentControlPoint(const Point2D &point);
/** \brief Returns the current number of 2D control points defining this figure. */
unsigned int GetNumberOfControlPoints() const;
/** \brief Returns the minimum number of control points needed to represent
* this figure.
*
* Must be implemented in sub-classes.
*/
virtual unsigned int GetMinimumNumberOfControlPoints() const = 0;
/** \brief Returns the maximum number of control points allowed for
* this figure (e.g. 3 for triangles).
*
* Must be implemented in sub-classes.
*/
virtual unsigned int GetMaximumNumberOfControlPoints() const = 0;
/** \brief Selects currently active control points. */
virtual bool SelectControlPoint(unsigned int index);
/** \brief Deselect control point; no control point active. */
virtual bool DeselectControlPoint();
/** \brief Return currently selected control point. */
virtual int GetSelectedControlPoint() const { return m_SelectedControlPoint; }
/** \brief Returns specified control point in 2D world coordinates. */
Point2D GetControlPoint(unsigned int index) const;
/**
* \brief Returns the id of the control-point that corresponds to the given
* polyline-point.
*/
virtual int GetControlPointForPolylinePoint(int indexOfPolylinePoint, int polyLineIndex) const;
/** \brief Returns specified control point in world coordinates. */
Point3D GetWorldControlPoint(unsigned int index) const;
/** \brief Returns the polyline representing the planar figure
* (for rendering, measurements, etc.). */
const PolyLineType GetPolyLine(unsigned int index);
/** \brief Returns the polyline representing the planar figure
- * (for rendering, measurments, etc.). */
+ * (for rendering, measurements, etc.). */
const PolyLineType GetPolyLine(unsigned int index) const;
/** \brief Returns the polyline that should be drawn the same size at every scale
* (for text, angles, etc.). */
const PolyLineType GetHelperPolyLine(unsigned int index, double mmPerDisplayUnit, unsigned int displayHeight);
/** \brief Sets the position of the PreviewControlPoint. Automatically sets it visible.*/
void SetPreviewControlPoint(const Point2D &point);
/** \brief Marks the PreviewControlPoint as invisible.*/
void ResetPreviewContolPoint();
/** \brief Returns whether or not the PreviewControlPoint is visible.*/
bool IsPreviewControlPointVisible() const;
/** \brief Returns the coordinates of the PreviewControlPoint. */
Point2D GetPreviewControlPoint() const;
/** \brief Returns the number of features available for this PlanarFigure
* (such as, radius, area, ...). */
virtual unsigned int GetNumberOfFeatures() const;
/** \brief Returns the name (identifier) of the specified features. */
const char *GetFeatureName(unsigned int index) const;
/** \brief Returns the physical unit of the specified features. */
const char *GetFeatureUnit(unsigned int index) const;
/** Returns quantity of the specified feature (e.g., length, radius,
* area, ... ) */
double GetQuantity(unsigned int index) const;
/** \brief Returns true if the feature with the specified index exists and
* is active (an inactive feature may e.g. be the area of a non-closed
* polygon. */
bool IsFeatureActive(unsigned int index) const;
/** \brief Returns true if the feature with the specified index exists and is set visible */
bool IsFeatureVisible(unsigned int index) const;
/** \brief Defines if the feature with the specified index will be shown as an
* Annotation in the RenderWindow */
void SetFeatureVisible(unsigned int index, bool visible);
/** \brief Calculates quantities of all features of this planar figure. */
virtual void EvaluateFeatures();
/** \brief Intherited from parent */
void UpdateOutputInformation() override;
/** \brief Intherited from parent */
void SetRequestedRegionToLargestPossibleRegion() override;
/** \brief Intherited from parent */
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/** \brief Intherited from parent */
bool VerifyRequestedRegion() override;
/** \brief Intherited from parent */
void SetRequestedRegion(const itk::DataObject *data) override;
/** \brief Returns the current number of polylines */
virtual unsigned short GetPolyLinesSize();
/** \brief Returns the current number of helperpolylines */
virtual unsigned short GetHelperPolyLinesSize() const;
/** \brief Returns whether a helper polyline should be painted or not */
virtual bool IsHelperToBePainted(unsigned int index) const;
/** \brief Returns true if the planar figure is reset to "add points" mode
* when a point is selected.
*
* Default return value is false. Subclasses can overwrite this method and
* execute any reset / initialization statements required. */
virtual bool ResetOnPointSelect();
virtual bool ResetOnPointSelectNeeded() const;
/** \brief removes the point with the given index from the list of controlpoints. */
virtual void RemoveControlPoint(unsigned int index);
/** \brief Removes last control point */
virtual void RemoveLastControlPoint();
/** \brief Allow sub-classes to apply constraints on control points.
*
* Sub-classes can define spatial constraints to certain control points by
* overwriting this method and returning a constrained point. By default,
* the points are constrained by the image bounds. */
virtual Point2D ApplyControlPointConstraints(unsigned int /*index*/, const Point2D &point);
/**
* \brief Compare two PlanarFigure objects
* Note: all subclasses have to implement the method on their own.
*/
virtual bool Equals(const mitk::PlanarFigure &other) const;
/** \brief Set the initial number of control points of the planar figure */
void ResetNumberOfControlPoints(int numberOfControlPoints);
protected:
PlanarFigure();
PlanarFigure(const Self &other);
/** Adds feature (e.g., circumference, radius, angle, ...) to feature vector
* of a planar figure object and returns integer ID for the feature element.
* Should be called in sub-class constructors. */
virtual unsigned int AddFeature(const char *featureName, const char *unitName);
/** Sets the name of the specified feature. INTERNAL METHOD. */
void SetFeatureName(unsigned int index, const char *featureName);
/** Sets the physical unit of the specified feature. INTERNAL METHOD. */
void SetFeatureUnit(unsigned int index, const char *unitName);
/** Sets quantity of the specified feature. INTERNAL METHOD. */
void SetQuantity(unsigned int index, double quantity);
/** Sets the specified feature as active. INTERAL METHOD. */
void ActivateFeature(unsigned int index);
/** Sets the specified feature as active. INTERAL METHOD. */
void DeactivateFeature(unsigned int index);
/** \brief Generates the poly-line representation of the planar figure.
* Must be implemented in sub-classes. */
virtual void GeneratePolyLine() = 0;
/** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.
* Must be implemented in sub-classes. */
virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) = 0;
/** \brief Calculates quantities of all features of this planar figure.
* Must be implemented in sub-classes. */
virtual void EvaluateFeaturesInternal() = 0;
/** \brief Initializes the TimeGeometry describing the (time-resolved)
* geometry of this figure. Note that each time step holds one PlaneGeometry.
*/
void InitializeTimeGeometry(unsigned int timeSteps = 1) override;
/** \brief defines the number of PolyLines that will be available */
void SetNumberOfPolyLines(unsigned int numberOfPolyLines);
/** \brief Append a point to the PolyLine # index */
void AppendPointToPolyLine(unsigned int index, PolyLineElement element);
/** \brief clears the list of PolyLines. Call before re-calculating a new Polyline. */
void ClearPolyLines();
/** \brief defines the number of HelperPolyLines that will be available */
void SetNumberOfHelperPolyLines(unsigned int numberOfHelperPolyLines);
/** \brief Append a point to the HelperPolyLine # index */
void AppendPointToHelperPolyLine(unsigned int index, PolyLineElement element);
/** \brief clears the list of HelperPolyLines. Call before re-calculating a new HelperPolyline. */
void ClearHelperPolyLines();
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
ControlPointListType m_ControlPoints;
unsigned int m_NumberOfControlPoints;
// Currently selected control point; -1 means no point selected
int m_SelectedControlPoint;
std::vector<PolyLineType> m_PolyLines;
std::vector<PolyLineType> m_HelperPolyLines;
BoolContainerType::Pointer m_HelperPolyLinesToBePainted;
- // this point is used to store the coordiantes an additional 'ControlPoint' that is rendered
+ // this point is used to store the coordinates an additional 'ControlPoint' that is rendered
// when the mouse cursor is above the figure (and not a control-point) and when the
// property 'planarfigure.isextendable' is set to true
Point2D m_PreviewControlPoint;
bool m_PreviewControlPointVisible;
bool m_FigurePlaced;
private:
// not implemented to prevent PlanarFigure::New() calls which would create an itk::Object.
static Pointer New();
struct Feature
{
Feature(const char *name, const char *unit) : Name(name), Unit(unit), Quantity(0.0), Active(true), Visible(true)
{
}
std::string Name;
std::string Unit;
double Quantity;
bool Active;
bool Visible;
};
itk::LightObject::Pointer InternalClone() const override = 0;
bool m_PolyLineUpToDate;
bool m_HelperLinesUpToDate;
bool m_FeaturesUpToDate;
// Vector of features available for this geometric figure
typedef std::vector<Feature> FeatureVectorType;
FeatureVectorType m_Features;
unsigned long m_FeaturesMTime;
// this pair is used to store the mmInDisplayUnits (m_DisplaySize.first) and the displayHeight
// (m_DisplaySize.second)
// that the helperPolyLines have been calculated for.
// It's used to determine whether or not GetHelperPolyLine() needs to recalculate the HelperPolyLines.
std::pair<double, unsigned int> m_DisplaySize;
};
MITKPLANARFIGURE_EXPORT bool Equal(const mitk::PlanarFigure &leftHandSide,
const mitk::PlanarFigure &rightHandSide,
ScalarType eps,
bool verbose);
} // namespace mitk
#endif
diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureSource.h b/Modules/PlanarFigure/include/mitkPlanarFigureSource.h
index 24c3f99b9f..1404755900 100644
--- a/Modules/PlanarFigure/include/mitkPlanarFigureSource.h
+++ b/Modules/PlanarFigure/include/mitkPlanarFigureSource.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#ifndef mitkPlanarFigureSource_h
#define mitkPlanarFigureSource_h
#include "mitkBaseDataSource.h"
#include "mitkCommon.h"
#include "mitkPlanarFigure.h"
#include <MitkPlanarFigureExports.h>
namespace mitk
{
/**
* @brief Base class for all filters which have an object of type
* mitk::PlanarFigure as output
*
* Base class for all filters which have an object of type mitk::PlanarFigure
* as output. mitk::PlanarFigureSources do not provide support
* for streaming, that is, that the requested region is always the largest
* possible region.
* @ingroup MitkPlanarFigureModule
*/
class MITKPLANARFIGURE_EXPORT PlanarFigureSource : public mitk::BaseDataSource
{
public:
mitkClassMacro(PlanarFigureSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::PlanarFigure OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef itk::DataObject::Pointer DataObjectPointer;
mitkBaseDataSourceGetOutputDeclarations
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
/**
* Generates the input requested region simply by calling the equivalent
* method of the superclass.
*/
void GenerateInputRequestedRegion() override;
protected:
PlanarFigureSource();
~PlanarFigureSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp
index b0222f645e..77cc54a45c 100644
--- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp
+++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp
@@ -1,271 +1,271 @@
/*============================================================================
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 "mitkPlanarPolygon.h"
#include "mitkPlaneGeometry.h"
#include "mitkProperties.h"
// stl related includes
#include <algorithm>
mitk::PlanarPolygon::PlanarPolygon()
: FEATURE_ID_CIRCUMFERENCE(this->AddFeature("Circumference", "mm")), FEATURE_ID_AREA(this->AddFeature("Area", "mm2"))
{
// Polygon has at least two control points
this->ResetNumberOfControlPoints(2);
this->SetNumberOfPolyLines(1);
// Polygon is closed by default
this->SetProperty("closed", mitk::BoolProperty::New(true));
this->SetProperty("subdivision", mitk::BoolProperty::New(false));
}
void mitk::PlanarPolygon::SetClosed(bool closed)
{
this->SetProperty("closed", mitk::BoolProperty::New(closed));
if (!closed)
{
// For non-closed polygons: use "Length" as feature name; disable area
this->SetFeatureName(FEATURE_ID_CIRCUMFERENCE, "Length");
this->DeactivateFeature(FEATURE_ID_AREA);
}
else
{
// For closed polygons: use "Circumference" as feature name; enable area
this->SetFeatureName(FEATURE_ID_CIRCUMFERENCE, "Circumference");
this->ActivateFeature(FEATURE_ID_AREA);
}
this->Modified();
}
void mitk::PlanarPolygon::GeneratePolyLine()
{
this->ClearPolyLines();
for (ControlPointListType::size_type i = 0; i < m_ControlPoints.size(); ++i)
this->AppendPointToPolyLine(0, this->GetControlPoint(i));
}
void mitk::PlanarPolygon::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/)
{
// A polygon does not require helper objects
}
void mitk::PlanarPolygon::EvaluateFeaturesInternal()
{
// Calculate circumference
double circumference = 0.0;
unsigned int i, j;
const PolyLineType polyLine = m_PolyLines[0];
if (polyLine.empty())
return;
for (i = 0; i < (polyLine.size() - 1); ++i)
{
circumference += static_cast<Point2D>(polyLine[i]).EuclideanDistanceTo(static_cast<Point2D>(polyLine[i + 1]));
}
if (this->IsClosed())
{
circumference += static_cast<Point2D>(polyLine[i]).EuclideanDistanceTo(static_cast<Point2D>(polyLine.front()));
}
this->SetQuantity(FEATURE_ID_CIRCUMFERENCE, circumference);
// Calculate polygon area (if closed)
double area = 0.0;
bool intersection = false;
if (this->IsClosed() && (this->GetPlaneGeometry() != nullptr))
{
// does PlanarPolygon overlap/intersect itself?
const unsigned int numberOfPoints = polyLine.size();
if (numberOfPoints >= 4)
{
for (i = 0; i < (numberOfPoints - 1); ++i)
{
// line 1
const Point2D p0 = polyLine[i];
const Point2D p1 = polyLine[i + 1];
// check for intersection with all other lines
for (j = i + 1; j < (numberOfPoints - 1); ++j)
{
const Point2D p2 = polyLine[j];
const Point2D p3 = polyLine[j + 1];
intersection = CheckForLineIntersection(p0, p1, p2, p3);
if (intersection)
break;
}
if (intersection)
break; // only because the inner loop might have changed "intersection"
// last line from p_x to p_0
const Point2D p2 = polyLine.front();
const Point2D p3 = polyLine.back();
intersection = CheckForLineIntersection(p0, p1, p2, p3);
if (intersection)
break;
}
}
// calculate area
for (i = 0; i < polyLine.size(); ++i)
{
const Point2D p0 = polyLine[i];
const Point2D p1 = polyLine[(i + 1) % polyLine.size()];
area += p0[0] * p1[1] - p1[0] * p0[1];
}
area /= 2.0;
}
- // set area if appropiate (i.e. closed and not intersected)
+ // set area if appropriate (i.e. closed and not intersected)
if (this->IsClosed() && !intersection)
{
SetQuantity(FEATURE_ID_AREA, fabs(area));
this->ActivateFeature(FEATURE_ID_AREA);
}
else
{
SetQuantity(FEATURE_ID_AREA, 0);
this->DeactivateFeature(FEATURE_ID_AREA);
}
}
void mitk::PlanarPolygon::PrintSelf(std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
if (this->IsClosed())
os << indent << "Polygon is closed\n";
else
os << indent << "Polygon is not closed\n";
}
// based on
// https://flassari.is/2008/11/line-line-intersection-in-cplusplus/
bool mitk::PlanarPolygon::CheckForLineIntersection(const mitk::Point2D &p1,
const mitk::Point2D &p2,
const mitk::Point2D &p3,
const mitk::Point2D &p4,
Point2D &intersection) const
{
// do not check for intersections with control points
if (p1 == p2 || p1 == p3 || p1 == p4 || p2 == p3 || p2 == p4 || p3 == p4)
return false;
// Store the values for fast access and easy
// equations-to-code conversion
const double x1 = p1[0], x2 = p2[0], x3 = p3[0], x4 = p4[0];
const double y1 = p1[1], y2 = p2[1], y3 = p3[1], y4 = p4[1];
const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
// If d is zero, there is no intersection
// if (d < mitk::eps) return false;
if (d == 0)
return false;
// Get the x and y
const double pre = (x1 * y2 - y1 * x2);
const double post = (x3 * y4 - y3 * x4);
const double x = (pre * (x3 - x4) - (x1 - x2) * post) / d;
const double y = (pre * (y3 - y4) - (y1 - y2) * post) / d;
double tolerance = 0.001;
// Check if the x coordinates are within both lines, including tolerance
if (x < (std::min(x1, x2) - tolerance) || x > (std::max(x1, x2) + tolerance) || x < (std::min(x3, x4) - tolerance) ||
x > (std::max(x3, x4) + tolerance))
{
return false;
}
// Check if the y coordinates are within both lines, including tolerance
if (y < (std::min(y1, y2) - tolerance) || y > (std::max(y1, y2) + tolerance) || y < (std::min(y3, y4) - tolerance) ||
y > (std::max(y3, y4) + tolerance))
{
return false;
}
// point of intersection
Point2D ret;
ret[0] = x;
ret[1] = y;
intersection = ret;
return true;
}
bool mitk::PlanarPolygon::CheckForLineIntersection(const mitk::Point2D &p1,
const mitk::Point2D &p2,
const mitk::Point2D &p3,
const mitk::Point2D &p4) const
{
mitk::Point2D intersection;
return mitk::PlanarPolygon::CheckForLineIntersection(p1, p2, p3, p4, intersection);
}
std::vector<mitk::Point2D> mitk::PlanarPolygon::CheckForLineIntersection(const mitk::Point2D &p1,
const mitk::Point2D &p2) const
{
std::vector<mitk::Point2D> intersectionList;
ControlPointListType polyLinePoints;
const PolyLineType tempList = m_PolyLines[0];
for (auto iter = tempList.cbegin(); iter != tempList.cend(); ++iter)
{
polyLinePoints.push_back(*iter);
}
for (ControlPointListType::size_type i = 0; i < polyLinePoints.size() - 1; i++)
{
const mitk::Point2D pnt1 = polyLinePoints[i];
const mitk::Point2D pnt2 = polyLinePoints[i + 1];
mitk::Point2D intersection;
if (mitk::PlanarPolygon::CheckForLineIntersection(p1, p2, pnt1, pnt2, intersection))
{
intersectionList.push_back(intersection);
}
}
if (this->IsClosed())
{
mitk::Point2D intersection;
const mitk::Point2D lastControlPoint = polyLinePoints.back();
const mitk::Point2D firstControlPoint = polyLinePoints.front();
if (mitk::PlanarPolygon::CheckForLineIntersection(lastControlPoint, firstControlPoint, p1, p2, intersection))
{
intersectionList.push_back(intersection);
}
}
return intersectionList;
}
bool mitk::PlanarPolygon::Equals(const mitk::PlanarFigure &other) const
{
const auto *otherPolygon = dynamic_cast<const mitk::PlanarPolygon *>(&other);
if (otherPolygon)
{
return Superclass::Equals(other);
}
else
{
return false;
}
}
diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.cpp b/Modules/Python/autoload/PythonService/mitkPythonService.cpp
index a8df58ec85..636530ec4a 100644
--- a/Modules/Python/autoload/PythonService/mitkPythonService.cpp
+++ b/Modules/Python/autoload/PythonService/mitkPythonService.cpp
@@ -1,744 +1,744 @@
/*============================================================================
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 "mitkPythonService.h"
#include <Python.h>
#include <mitkIOUtil.h>
#include <QFile>
#include <QDir>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 5208)
#endif
#include <PythonQt.h>
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#include "PythonPath.h"
#include <vtkPolyData.h>
#include <mitkRenderingManager.h>
#include <mitkImageReadAccessor.h>
#include <mitkImageWriteAccessor.h>
#include <QFileInfo>
#include <QCoreApplication>
#include <itksys/SystemTools.hxx>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
#include <mitkExceptionMacro.h>
#ifndef WIN32
#include <dlfcn.h>
#endif
typedef itksys::SystemTools ist;
mitk::PythonService::PythonService()
: m_ItkWrappingAvailable( true )
, m_VtkWrappingAvailable( true )
, m_ErrorOccured( false )
{
bool pythonInitialized = static_cast<bool>( Py_IsInitialized() ); //m_PythonManager.isPythonInitialized() );
// due to strange static var behaviour on windows Py_IsInitialized() returns correct value while
// m_PythonManager.isPythonInitialized() does not because it has been constructed and destructed again
if( !pythonInitialized )
{
MITK_INFO << "Initializing python service";
//TODO a better way to do this
#ifndef WIN32
dlerror();
if(dlopen(PYTHON_LIBRARY, RTLD_NOW | RTLD_GLOBAL) == nullptr )
{
mitkThrow() << "Python runtime could not be loaded: " << dlerror();
}
#endif
std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/";
QString pythonCommand;
pythonCommand.append( QString("import site, sys\n") );
pythonCommand.append( QString("import SimpleITK as sitk\n") );
pythonCommand.append( QString("import SimpleITK._SimpleITK as _SimpleITK\n") );
pythonCommand.append( QString("import numpy\n") );
pythonCommand.append( QString("sys.path.append('')\n") );
pythonCommand.append( QString("sys.path.append('%1')\n").arg(programPath.c_str()) );
pythonCommand.append( QString("sys.path.append('%1')\n").arg(EXTERNAL_DIST_PACKAGES) );
pythonCommand.append( QString("\nsite.addsitedir('%1')").arg(EXTERNAL_SITE_PACKAGES) );
if( pythonInitialized )
m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut|PythonQt::PythonAlreadyInitialized);
else
m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut);
m_PythonManager.initialize();
m_PythonManager.executeString( pythonCommand, ctkAbstractPythonManager::FileInput );
}
}
mitk::PythonService::~PythonService()
{
MITK_DEBUG("mitk::PythonService") << "destructing PythonService";
}
void mitk::PythonService::AddRelativeSearchDirs(std::vector< std::string > dirs)
{
std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/";
std::string cwd = ist::GetCurrentWorkingDirectory() + "/";
for (auto dir : dirs)
{
m_PythonManager.executeString(QString("sys.path.append('%1')").arg((programPath + dir).c_str()), ctkAbstractPythonManager::SingleInput );
m_PythonManager.executeString(QString("sys.path.append('%1')").arg((cwd + dir).c_str()), ctkAbstractPythonManager::SingleInput );
}
}
void mitk::PythonService::AddAbsoluteSearchDirs(std::vector< std::string > dirs)
{
for (auto dir : dirs)
{
m_PythonManager.executeString(QString("sys.path.append('%1')").arg(dir.c_str()), ctkAbstractPythonManager::SingleInput );
}
}
std::string mitk::PythonService::Execute(const std::string &stdpythonCommand, int commandType)
{
QString pythonCommand = QString::fromStdString(stdpythonCommand);
QVariant result;
bool commandIssued = true;
if(commandType == IPythonService::SINGLE_LINE_COMMAND )
result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::SingleInput );
else if(commandType == IPythonService::MULTI_LINE_COMMAND )
result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::FileInput );
else if(commandType == IPythonService::EVAL_COMMAND )
result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::EvalInput );
else
commandIssued = false;
if(commandIssued)
{
this->NotifyObserver(pythonCommand.toStdString());
m_ErrorOccured = PythonQt::self()->hadError();
}
return result.toString().toStdString();
}
void mitk::PythonService::ExecuteScript( const std::string& pythonScript )
{
std::ifstream t(pythonScript.c_str());
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
t.close();
m_PythonManager.executeString(QString::fromStdString(str));
}
std::vector<mitk::PythonVariable> mitk::PythonService::GetVariableStack() const
{
std::vector<mitk::PythonVariable> list;
PyObject* dict = PyImport_GetModuleDict();
PyObject* object = PyDict_GetItemString(dict, "__main__");
PyObject* dirMain = PyObject_Dir(object);
PyObject* tempObject = nullptr;
//PyObject* strTempObject = 0;
if(dirMain)
{
std::string name, attrValue, attrType;
for(int i = 0; i<PyList_Size(dirMain); i++)
{
tempObject = PyList_GetItem(dirMain, i);
name = PyString_AsString(tempObject);
tempObject = PyObject_GetAttrString( object, name.c_str() );
attrType = tempObject->ob_type->tp_name;
if(tempObject && ( PyUnicode_Check(tempObject) || PyString_Check(tempObject) ) )
attrValue = PyString_AsString(tempObject);
else
attrValue = "";
mitk::PythonVariable var;
var.m_Name = name;
var.m_Value = attrValue;
var.m_Type = attrType;
list.push_back(var);
}
}
return list;
}
std::string mitk::PythonService::GetVariable(const std::string& name) const
{
std::vector<mitk::PythonVariable> allVars = this->GetVariableStack();
for(unsigned int i = 0; i< allVars.size(); i++)
{
if( allVars.at(i).m_Name == name )
return allVars.at(i).m_Value;
}
return "";
}
bool mitk::PythonService::DoesVariableExist(const std::string& name) const
{
bool varExists = false;
std::vector<mitk::PythonVariable> allVars = this->GetVariableStack();
for(unsigned int i = 0; i< allVars.size(); i++)
{
if( allVars.at(i).m_Name == name )
{
varExists = true;
break;
}
}
return varExists;
}
void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer)
{
if(!m_Observer.contains(observer))
m_Observer.append(observer);
}
void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer)
{
m_Observer.removeOne(observer);
}
void mitk::PythonService::NotifyObserver(const std::string &command)
{
MITK_DEBUG("mitk::PythonService") << "number of observer " << m_Observer.size();
for( int i=0; i< m_Observer.size(); ++i )
{
m_Observer.at(i)->CommandExecuted(command);
}
}
bool mitk::PythonService::CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName)
{
QString varName = QString::fromStdString( stdvarName );
QString command;
unsigned int* imgDim = image->GetDimensions();
int npy_nd = 1;
// access python module
PyObject *pyMod = PyImport_AddModule("__main__");
// global dictionary
PyObject *pyDict = PyModule_GetDict(pyMod);
const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing();
const mitk::Point3D origin = image->GetGeometry()->GetOrigin();
mitk::PixelType pixelType = image->GetPixelType();
auto ioPixelType = image->GetPixelType().GetPixelType();
PyObject* npyArray = nullptr;
mitk::ImageReadAccessor racc(image);
void* array = const_cast<void*>(racc.GetData());
mitk::Vector3D xDirection;
mitk::Vector3D yDirection;
mitk::Vector3D zDirection;
const vnl_matrix_fixed<ScalarType, 3, 3> &transform =
image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix();
mitk::Vector3D s = image->GetGeometry()->GetSpacing();
- // ToDo: Check if this is a collumn or row vector from the matrix.
+ // ToDo: Check if this is a column or row vector from the matrix.
// right now it works but not sure for rotated geometries
mitk::FillVector3D(xDirection, transform[0][0]/s[0], transform[0][1]/s[1], transform[0][2]/s[2]);
mitk::FillVector3D(yDirection, transform[1][0]/s[0], transform[1][1]/s[1], transform[1][2]/s[2]);
mitk::FillVector3D(zDirection, transform[2][0]/s[0], transform[2][1]/s[1], transform[2][2]/s[2]);
// save the total number of elements here (since the numpy array is one dimensional)
npy_intp* npy_dims = new npy_intp[1];
npy_dims[0] = imgDim[0];
/**
* Build a string in the format [1024,1028,1]
* to describe the dimensionality. This is needed for simple itk
* to know the dimensions of the image
*/
QString dimensionString;
dimensionString.append(QString("["));
dimensionString.append(QString::number(imgDim[0]));
for (unsigned i = 1; i < 3; ++i)
// always three because otherwise the 3d-geometry gets destroyed
// (relevant for backtransformation of simple itk image to mitk.
{
dimensionString.append(QString(","));
dimensionString.append(QString::number(imgDim[i]));
npy_dims[0] *= imgDim[i];
}
dimensionString.append("]");
// the next line is necessary for vectorimages
npy_dims[0] *= pixelType.GetNumberOfComponents();
// default pixeltype: unsigned short
NPY_TYPES npy_type = NPY_USHORT;
std::string sitk_type = "sitkUInt8";
if( ioPixelType == itk::IOPixelEnum::SCALAR )
{
if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
sitk_type = "sitkFloat64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
sitk_type = "sitkFloat32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
sitk_type = "sitkInt16";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
sitk_type = "sitkInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
sitk_type = "sitkInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
sitk_type = "sitkUInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
sitk_type = "sitkUInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkUInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) {
npy_type = NPY_USHORT;
sitk_type = "sitkUInt16";
}
}
else if ( ioPixelType == itk::IOPixelEnum::VECTOR ||
ioPixelType == itk::IOPixelEnum::RGB ||
ioPixelType == itk::IOPixelEnum::RGBA
)
{
if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
sitk_type = "sitkVectorFloat64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
sitk_type = "sitkVectorFloat32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
sitk_type = "sitkVectorInt16";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
sitk_type = "sitkVectorInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
sitk_type = "sitkVectorInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkVectorInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
sitk_type = "sitkVectorUInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
sitk_type = "sitkVectorUInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkVectorUInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) {
npy_type = NPY_USHORT;
sitk_type = "sitkVectorUInt16";
}
}
else {
MITK_WARN << "not a recognized pixeltype";
return false;
}
// creating numpy array
import_array1 (true);
npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array);
// add temp array it to the python dictionary to access it in python code
const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array")
.arg(varName).toStdString().c_str(),
npyArray );
// sanity check
if ( status != 0 )
return false;
command.append( QString("%1 = sitk.Image(%2,sitk.%3,%4)\n").arg(varName)
.arg(dimensionString)
.arg(QString(sitk_type.c_str())).arg(QString::number(pixelType.GetNumberOfComponents())) );
command.append( QString("%1.SetSpacing([%2,%3,%4])\n").arg(varName)
.arg(QString::number(spacing[0]))
.arg(QString::number(spacing[1]))
.arg(QString::number(spacing[2])) );
command.append( QString("%1.SetOrigin([%2,%3,%4])\n").arg(varName)
.arg(QString::number(origin[0]))
.arg(QString::number(origin[1]))
.arg(QString::number(origin[2])) );
command.append( QString("%1.SetDirection([%2,%3,%4,%5,%6,%7,%8,%9,%10])\n").arg(varName)
.arg(QString::number(xDirection[0]))
.arg(QString::number(xDirection[1]))
.arg(QString::number(xDirection[2]))
.arg(QString::number(yDirection[0]))
.arg(QString::number(yDirection[1]))
.arg(QString::number(yDirection[2]))
.arg(QString::number(zDirection[0]))
.arg(QString::number(zDirection[1]))
.arg(QString::number(zDirection[2]))
);
// directly access the cpp api from the lib
command.append( QString("_SimpleITK._SetImageFromArray(%1_numpy_array,%1)\n").arg(varName) );
command.append( QString("del %1_numpy_array").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return true;
}
mitk::PixelType DeterminePixelType(const std::string& pythonPixeltype, unsigned long nrComponents, int dimensions)
{
typedef itk::RGBPixel< unsigned char > UCRGBPixelType;
typedef itk::RGBPixel< unsigned short > USRGBPixelType;
typedef itk::RGBPixel< float > FloatRGBPixelType;
typedef itk::RGBPixel< double > DoubleRGBPixelType;
typedef itk::Image< UCRGBPixelType > UCRGBImageType;
typedef itk::Image< USRGBPixelType > USRGBImageType;
typedef itk::Image< FloatRGBPixelType > FloatRGBImageType;
typedef itk::Image< DoubleRGBPixelType > DoubleRGBImageType;
typedef itk::RGBAPixel< unsigned char > UCRGBAPixelType;
typedef itk::RGBAPixel< unsigned short > USRGBAPixelType;
typedef itk::RGBAPixel< float > FloatRGBAPixelType;
typedef itk::RGBAPixel< double > DoubleRGBAPixelType;
typedef itk::Image< UCRGBAPixelType > UCRGBAImageType;
typedef itk::Image< USRGBAPixelType > USRGBAImageType;
typedef itk::Image< FloatRGBAPixelType > FloatRGBAImageType;
typedef itk::Image< DoubleRGBAPixelType > DoubleRGBAImageType;
auto pixelType = mitk::MakePixelType<char, char >(nrComponents);
if (nrComponents == 1)
{
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<double, double >(nrComponents);
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<float, float >(nrComponents);
} else if( pythonPixeltype.compare("int16") == 0) {
pixelType = mitk::MakePixelType<short, short >(nrComponents);
} else if( pythonPixeltype.compare("int8") == 0 ) {
pixelType = mitk::MakePixelType<char, char >(nrComponents);
} else if( pythonPixeltype.compare("int32") == 0 ) {
pixelType = mitk::MakePixelType<int, int >(nrComponents);
} else if( pythonPixeltype.compare("int64") == 0 ) {
pixelType = mitk::MakePixelType<long, long >(nrComponents);
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<unsigned char, unsigned char >(nrComponents);
} else if( pythonPixeltype.compare("uint32") == 0 ) {
pixelType = mitk::MakePixelType<unsigned int, unsigned int >(nrComponents);
} else if( pythonPixeltype.compare("uint64") == 0 ) {
pixelType = mitk::MakePixelType<unsigned long, unsigned long >(nrComponents);
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<unsigned short, unsigned short >(nrComponents);
}
else
{
mitkThrow()<< "unknown scalar PixelType";
}
} else if(nrComponents == 3 && dimensions == 2) {
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<DoubleRGBImageType>();
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<FloatRGBImageType>();
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<UCRGBImageType>();
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<USRGBImageType>();
}
} else if( (nrComponents == 4) && dimensions == 2 ) {
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<DoubleRGBAImageType>();
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<FloatRGBAImageType>();
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<UCRGBAImageType>();
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<USRGBAImageType>();
}
}
else {
if( pythonPixeltype.compare("float64") == 0 ) {
pixelType = mitk::MakePixelType<double, itk::Vector<double,3> >(nrComponents);
} else if( pythonPixeltype.compare("float32") == 0 ) {
pixelType = mitk::MakePixelType<float, itk::Vector<float,3> >(nrComponents);
} else if( pythonPixeltype.compare("int16") == 0) {
pixelType = mitk::MakePixelType<short, itk::Vector<short,3> >(nrComponents);
} else if( pythonPixeltype.compare("int8") == 0 ) {
pixelType = mitk::MakePixelType<char, itk::Vector<char,3> >(nrComponents);
} else if( pythonPixeltype.compare("int32") == 0 ) {
pixelType = mitk::MakePixelType<int, itk::Vector<int,3> >(nrComponents);
} else if( pythonPixeltype.compare("int64") == 0 ) {
pixelType = mitk::MakePixelType<long, itk::Vector<long,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint8") == 0 ) {
pixelType = mitk::MakePixelType<unsigned char, itk::Vector<unsigned char,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint16") == 0 ) {
pixelType = mitk::MakePixelType<unsigned short, itk::Vector<unsigned short,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint32") == 0 ) {
pixelType = mitk::MakePixelType<unsigned int, itk::Vector<unsigned int,3> >(nrComponents);
} else if( pythonPixeltype.compare("uint64") == 0 ) {
pixelType = mitk::MakePixelType<unsigned long, itk::Vector<unsigned long,3> >(nrComponents);
} else {
mitkThrow()<< "unknown vectorial PixelType";
}
}
return pixelType;
}
mitk::Image::Pointer mitk::PythonService::CopySimpleItkImageFromPython(const std::string &stdvarName)
{
double*ds = nullptr;
// access python module
PyObject *pyMod = PyImport_AddModule("__main__");
// global dictionarry
PyObject *pyDict = PyModule_GetDict(pyMod);
mitk::Image::Pointer mitkImage = mitk::Image::New();
mitk::Vector3D spacing;
mitk::Point3D origin;
QString command;
QString varName = QString::fromStdString( stdvarName );
command.append( QString("%1_numpy_array = sitk.GetArrayFromImage(%1)\n").arg(varName) );
command.append( QString("%1_spacing = numpy.asarray(%1.GetSpacing())\n").arg(varName) );
command.append( QString("%1_origin = numpy.asarray(%1.GetOrigin())\n").arg(varName) );
command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) );
command.append( QString("%1_direction = numpy.asarray(%1.GetDirection())\n").arg(varName) );
command.append( QString("%1_nrComponents = numpy.asarray(%1.GetNumberOfComponentsPerPixel())\n").arg(varName));
command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() );
std::string dtype = PyString_AsString(py_dtype);
PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_numpy_array").arg(varName).toStdString().c_str() );
PyArrayObject* py_spacing = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_spacing").arg(varName).toStdString().c_str() );
PyArrayObject* py_origin = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_origin").arg(varName).toStdString().c_str() );
PyArrayObject* py_direction = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_direction").arg(varName).toStdString().c_str() );
PyArrayObject* py_nrComponents = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_nrComponents").arg(varName).toStdString().c_str() );
unsigned int nr_Components = *(reinterpret_cast<unsigned int*>(PyArray_DATA(py_nrComponents)));
unsigned int nr_dimensions = PyArray_NDIM(py_data);
if (nr_Components > 1) // for VectorImages the last dimension in the numpy array are the vector components.
{
--nr_dimensions;
}
mitk::PixelType pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions);
unsigned int* dimensions = new unsigned int[nr_dimensions];
// fill backwards , nd data saves dimensions in opposite direction
for( unsigned i = 0; i < nr_dimensions; ++i )
{
dimensions[i] = PyArray_DIMS(py_data)[nr_dimensions - 1 - i];
}
mitkImage->Initialize(pixelType, nr_dimensions, dimensions);
mitkImage->SetChannel(PyArray_DATA(py_data));
ds = reinterpret_cast<double*>(PyArray_DATA(py_spacing));
spacing[0] = ds[0];
spacing[1] = ds[1];
spacing[2] = ds[2];
mitkImage->GetGeometry()->SetSpacing(spacing);
ds = reinterpret_cast<double*>(PyArray_DATA(py_origin));
origin[0] = ds[0];
origin[1] = ds[1];
origin[2] = ds[2];
mitkImage->GetGeometry()->SetOrigin(origin);
itk::Matrix<double,3,3> py_transform;
ds = reinterpret_cast<double*>(PyArray_DATA(py_direction));
py_transform[0][0] = ds[0];
py_transform[0][1] = ds[1];
py_transform[0][2] = ds[2];
py_transform[1][0] = ds[3];
py_transform[1][1] = ds[4];
py_transform[1][2] = ds[5];
py_transform[2][0] = ds[6];
py_transform[2][1] = ds[7];
py_transform[2][2] = ds[8];
mitk::AffineTransform3D::Pointer affineTransform = mitkImage->GetGeometry()->GetIndexToWorldTransform();
itk::Matrix<double,3,3> transform = py_transform * affineTransform->GetMatrix();
affineTransform->SetMatrix(transform);
mitkImage->GetGeometry()->SetIndexToWorldTransform(affineTransform);
// mitk::AffineTransform3D::New();
//mitkImage->GetGeometry()->SetIndexToWorldTransform();
// cleanup
command.clear();
command.append( QString("del %1_numpy_array\n").arg(varName) );
command.append( QString("del %1_dtype\n").arg(varName) );
command.append( QString("del %1_spacing\n").arg(varName) );
command.append( QString("del %1_origin\n").arg(varName) );
command.append( QString("del %1_direction\n").arg(varName) );
command.append( QString("del %1_nrComponents\n").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
delete[] dimensions;
return mitkImage;
}
ctkAbstractPythonManager *mitk::PythonService::GetPythonManager()
{
return &m_PythonManager;
}
mitk::Surface::Pointer mitk::PythonService::CopyVtkPolyDataFromPython( const std::string& stdvarName )
{
// access python module
PyObject *pyMod = PyImport_AddModule((char*)"__main__");
// global dictionarry
PyObject *pyDict = PyModule_GetDict(pyMod);
// python memory address
PyObject *pyAddr = nullptr;
// cpp address
size_t addr = 0;
mitk::Surface::Pointer surface = mitk::Surface::New();
QString command;
QString varName = QString::fromStdString( stdvarName );
command.append( QString("%1_addr_str = %1.GetAddressAsString(\"vtkPolyData\")\n").arg(varName) );
// remove 0x from the address
command.append( QString("%1_addr = int(%1_addr_str[5:],16)").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
// get address of the object
pyAddr = PyDict_GetItemString(pyDict,QString("%1_addr").arg(varName).toStdString().c_str());
// convert to long
addr = PyInt_AsLong(pyAddr);
MITK_DEBUG << "Python object address: " << addr;
// get the object
vtkPolyData* poly = (vtkPolyData*)((void*)addr);
surface->SetVtkPolyData(poly);
// delete helper variables from python stack
command = "";
command.append( QString("del %1_addr_str\n").arg(varName) );
command.append( QString("del %1_addr").arg(varName) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return surface;
}
bool mitk::PythonService::CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& stdvarName )
{
QString varName = QString::fromStdString( stdvarName );
std::ostringstream oss;
std::string addr = "";
QString command;
QString address;
oss << (void*) ( surface->GetVtkPolyData() );
// get the address
addr = oss.str();
// remove "0x"
address = QString::fromStdString(addr.substr(2));
command.append( QString("%1 = vtk.vtkPolyData(\"%2\")\n").arg(varName).arg(address) );
MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString();
this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND );
return true;
}
bool mitk::PythonService::IsSimpleItkPythonWrappingAvailable()
{
this->Execute( "import SimpleITK as sitk\n", IPythonService::SINGLE_LINE_COMMAND );
// directly access cpp lib
this->Execute( "import SimpleITK._SimpleITK as _SimpleITK\n", IPythonService::SINGLE_LINE_COMMAND );
m_ItkWrappingAvailable = !this->PythonErrorOccured();
// check for numpy
this->Execute( "import numpy\n", IPythonService::SINGLE_LINE_COMMAND );
if ( this->PythonErrorOccured() )
MITK_ERROR << "Numpy not found.";
m_ItkWrappingAvailable = !this->PythonErrorOccured();
return m_ItkWrappingAvailable;
}
bool mitk::PythonService::IsVtkPythonWrappingAvailable()
{
this->Execute( "import vtk", IPythonService::SINGLE_LINE_COMMAND );
//this->Execute( "print \"Using VTK version \" + vtk.vtkVersion.GetVTKVersion()\n", IPythonService::SINGLE_LINE_COMMAND );
m_VtkWrappingAvailable = !this->PythonErrorOccured();
return m_VtkWrappingAvailable;
}
bool mitk::PythonService::PythonErrorOccured() const
{
return m_ErrorOccured;
}
diff --git a/Modules/Python/documentation/mitkPython.dox b/Modules/Python/documentation/mitkPython.dox
index 08ad7e4ef1..e55e2da394 100644
--- a/Modules/Python/documentation/mitkPython.dox
+++ b/Modules/Python/documentation/mitkPython.dox
@@ -1,46 +1,46 @@
/**
\page mitkPython_Overview Python Module
\section python_sec1 Brief description
The MITK Python Module provides a service class to interactively run python code (passed as C++ strings) and
evaluate the results. Furthermore the service class offers means to convert an MITK Image to an ITK image in their wrapped python environment.
<strong>Thus, one can process MITK images with Python Code from the ITK wrapping system</strong>.
Furthermore one can convert an mitk::Surface to a vtkPolyData in its Python environment.<br />
Under the hood, the MITK build system takes care that the wrapping build process for SimpleITK/VTK is correctly initiated and all paths are correctly set within MITK code.
To use the features of the different toolkits make sure they are enabled during the superbuild process.
\section python_sec2 Build Instructions
Have a look at \ref python_sec3 on how to build MITK-Python with Qt6.
The following CMake build options are available:
<ul>
<li> MITK_USE_Python3
</ul>
\subsection python_ssec1 MITK_USE_Python3
MITK_USE_Python3 enables the python wrapping in MITK. When the option is activated
the build of the additional dependency SimpleITK is also enabled. The default behaviour is to use the python runtime from the system is used.
Only Python 3.x is supported.
The user can also specify it's own runtime by modifying the variables added by the
FindPythonLib.cmake script. <strong>Note:</strong> A Python runtime with numpy is needed to use the MITK Python wrapping.
When using this options all additional libraries installed in the python runtime will be available within the MITK-Python console.
-\section python_sec3 Suported Data Types
+\section python_sec3 Supported Data Types
The following data types in MITK are supported in the MITK Python Wrapping:
<ul>
<li> Image
<li> Surface
</ul>
\subsection python_ssec4 Image
Mitk Images can be transferred to python. The images are copied in-memory and
transferred as a numpy array to Python and vice versa. The MITK python wrapping creates a SimpleITK image
using the numpy array with the properties of the MITK Image.
\subsection python_ssec5 Surface
Surfaces within mitk can be transferred as a vtkPolyData Object to Python.
The surfaces are fully memory mapped. When changing a python wrapped surface
the original object is also modified on the C++ side of MITK.
*/
diff --git a/Modules/Python/documentation/slides.tex b/Modules/Python/documentation/slides.tex
index 5be2291ea0..1e5fc442c8 100644
--- a/Modules/Python/documentation/slides.tex
+++ b/Modules/Python/documentation/slides.tex
@@ -1,44 +1,44 @@
\documentclass{beamer}
\usepackage[utf8]{inputenc}
\usepackage{listings}
\usetheme{Warsaw}
\title[The MITK Python Module]{The MITK Python Module\\A short introduction}
\author{Michael Müller}
\institute{mitk.org}
\date{\today}
\begin{document}
\begin{frame}
\titlepage
\end{frame}
\begin{frame}{Problem}
Playing around with ITK/VTK/OpenCV functions in C++ is
\center{\textbf{time consuming}}
\\(setup project, write code, compile, adapt parameters, recompile, execute, do it again, do it again, Visual Studio crashes, do it again...)
\end{frame}
\begin{frame}{Aims}
\begin{itemize}
\item Use Python as an interpreted language to interactively use and test ITK/VTK/OpenCV functions \pause
\item Provide a one click super-build integration \pause
-\item Provide a service class to programatically execute and evaluate Python code \pause
+\item Provide a service class to programmatically execute and evaluate Python code \pause
\item Create a GUI to interactively work with Python within the MITK workbench
\end{itemize}
\end{frame}
\begin{frame}{The Python Module}
\textbf{Central class: IPythonService}\\
Most important functions:\\
\end{frame}
\begin{frame}{The Python View}
write motivation here
\end{frame}
\begin{frame}{Installation}
write motivation here
\end{frame}
\end{document}
diff --git a/Modules/Python/mitkIPythonService.h b/Modules/Python/mitkIPythonService.h
index 874f8e5629..f465cf846d 100644
--- a/Modules/Python/mitkIPythonService.h
+++ b/Modules/Python/mitkIPythonService.h
@@ -1,142 +1,142 @@
/*============================================================================
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 mitkIPythonService_h
#define mitkIPythonService_h
// mitk
#include <mitkImage.h>
#include <mitkSurface.h>
#include <MitkPythonExports.h>
//for microservices
#include <mitkServiceInterface.h>
#include <vector>
class ctkAbstractPythonManager;
namespace mitk
{
///
/// describes a python variable (data container)
/// \see IPythonService::GetVariableStack()
///
struct PythonVariable
{
std::string m_Name;
std::string m_Type;
std::string m_Value;
};
///
/// a PythonCommandObserver gets informed as soon as a python command was issued
/// \see IPythonService::AddPythonCommandObserver()
///
class PythonCommandObserver
{
public:
virtual void CommandExecuted(const std::string& pythonCommand) = 0;
};
///
/// The central service for issuing Python Code
/// The class also enables to transfer mitk images to python as itk::Image and vice versa
/// \see IPythonService::GetVariableStack()
///
class MITKPYTHON_EXPORT IPythonService
{
public:
///
/// Constant representing a single line command
/// \see IPythonService::Execute()
static const int SINGLE_LINE_COMMAND = 0;
///
- /// Constant representing a command in which the commands are seperated by new lines, i.e. "\\n"
+ /// Constant representing a command in which the commands are separated by new lines, i.e. "\\n"
/// \see IPythonService::Execute()
static const int MULTI_LINE_COMMAND = 1;
///
/// Constant representing a single line command x which is run as "eval(x)"
/// \see IPythonService::Execute()
static const int EVAL_COMMAND = 2;
///
/// Executes a python command.
/// \return A variant containing the return value as string of the python code (if any)
virtual std::string Execute( const std::string& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) = 0;
///
/// Executes a python script.
virtual void ExecuteScript( const std::string& pathToPythonScript ) = 0;
///
/// \return true if the last call to Execute...() resulted in an error, false otherwise
virtual bool PythonErrorOccured() const = 0;
///
/// \return The list of variables in the __main__ namespace
virtual std::vector<PythonVariable> GetVariableStack() const = 0;
///
/// \return true if a variable with this name is defined in the __main__ namespace, false otherwise
virtual bool DoesVariableExist(const std::string& name) const = 0;
///
/// \return value of variable with this name as string, empty string if variable does not exist
virtual std::string GetVariable(const std::string& name) const = 0;
///
/// adds a command observer which is informed after a command was issued with "Execute"
virtual void AddPythonCommandObserver( PythonCommandObserver* observer ) = 0;
///
/// removes a specific command observer
virtual void RemovePythonCommandObserver( PythonCommandObserver* observer ) = 0;
///
/// notify all observer. this should only be used if it can be garantueed that the
/// current python interpreter instance got another command from anywhere else
/// the the Execute() method of this service, e.g. the shell widget uses this function
/// since it does not use Execute()
virtual void NotifyObserver( const std::string& command ) = 0;
///
/// \return true, if itk wrapping is available, false otherwise
virtual bool IsSimpleItkPythonWrappingAvailable() = 0;
///
/// copies an mitk image as itk image into the python interpreter process
- /// the image will be available as "varName" in python if everythin worked
+ /// the image will be available as "varName" in python if everything worked
/// \return true if image was copied, else false
virtual bool CopyToPythonAsSimpleItkImage( mitk::Image* image, const std::string& varName ) = 0;
///
/// copies an itk image from the python process that is named "varName"
/// \return the image or 0 if copying was not possible
virtual mitk::Image::Pointer CopySimpleItkImageFromPython( const std::string& varName ) = 0;
///
/// \return true, if vtk wrapping is available, false otherwise
virtual bool IsVtkPythonWrappingAvailable() = 0;
///
/// \see CopyToPythonAsItkImage()
virtual bool CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& varName ) = 0;
/// \return the ctk abstract python manager instance
virtual ctkAbstractPythonManager* GetPythonManager() = 0;
///
/// nothing to do here
virtual ~IPythonService(); // leer in mitkIPythonService.cpp implementieren
// force us module loading by linking
static std::string ForceLoadModule();
virtual void AddRelativeSearchDirs(std::vector< std::string > dirs) = 0;
virtual void AddAbsoluteSearchDirs(std::vector< std::string > dirs) = 0;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::IPythonService, "org.mitk.services.IPythonService")
#endif
diff --git a/Modules/QtOverlays/QmitkOverlayContainerWidget.h b/Modules/QtOverlays/QmitkOverlayContainerWidget.h
index aad0d194fb..9f2165a6fe 100644
--- a/Modules/QtOverlays/QmitkOverlayContainerWidget.h
+++ b/Modules/QtOverlays/QmitkOverlayContainerWidget.h
@@ -1,48 +1,48 @@
/*============================================================================
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 QmitkOverlayContainerWidget_h
#define QmitkOverlayContainerWidget_h
// Qt
#include <QWidget>
#include <MitkQtOverlaysExports.h>
/**
* \class QmitkOverlayContainerWidget
* \brief Widget that overrides the paintEvent method to correctly display
* the Qt based overlays when using the system-environment variable
-* QT_DEVIDE_PIXEL_RATIO.
+* QT_DEVICE_PIXEL_RATIO.
*/
class MITKQTOVERLAYS_EXPORT QmitkOverlayContainerWidget : public QWidget
{
public:
/**
* @brief Default Constructor
**/
QmitkOverlayContainerWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
/**
* @brief Default Destructor
**/
~QmitkOverlayContainerWidget() override;
protected:
/**
* @brief overridden version of paintEvent that correctly clears its canvas before painting.
**/
void paintEvent(QPaintEvent *event) override;
};
#endif
diff --git a/Modules/QtPython/QmitkPythonTextEditor.h b/Modules/QtPython/QmitkPythonTextEditor.h
index 9ec929f1bf..92cb0096f6 100644
--- a/Modules/QtPython/QmitkPythonTextEditor.h
+++ b/Modules/QtPython/QmitkPythonTextEditor.h
@@ -1,49 +1,49 @@
/*============================================================================
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 QmitkPythonTextEditor_h
#define QmitkPythonTextEditor_h
#include <QTextEdit>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <MitkQtPythonExports.h>
struct QmitkPythonTextEditorData;
///
-/// this is a python text editor with syntax highlightning
+/// this is a python text editor with syntax highlighting
class MITKQTPYTHON_EXPORT QmitkPythonTextEditor : public QWidget
{
Q_OBJECT
public:
QmitkPythonTextEditor(QWidget *parent = nullptr);
~QmitkPythonTextEditor() override;
public slots:
void Paste(const QString& command);
protected slots:
void on_SaveScript_triggered(bool checked=false);
void on_LoadScript_triggered(bool checked=false);
void on_RunScript_triggered(bool checked=false);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
//bool canInsertFromMimeData( const QMimeData *source ) const;
QString ReadFile(const QString &filename);
private:
QmitkPythonTextEditorData* d;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h b/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h
index fda201043c..7abcad7a9f 100644
--- a/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h
+++ b/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h
@@ -1,266 +1,266 @@
/*============================================================================
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 QmitkAbstractNodeSelectionWidget_h
#define QmitkAbstractNodeSelectionWidget_h
#include <MitkQtWidgetsExports.h>
#include <mitkDataStorage.h>
#include <mitkWeakPointer.h>
#include <mitkNodePredicateBase.h>
#include <QWidget>
class QmitkAbstractDataStorageModel;
/**
* \class QmitkAbstractNodeSelectionWidget
* \brief Abstract base class for the selection of data from a data storage.
*/
class MITKQTWIDGETS_EXPORT QmitkAbstractNodeSelectionWidget : public QWidget
{
Q_OBJECT
public:
explicit QmitkAbstractNodeSelectionWidget(QWidget* parent = nullptr);
virtual ~QmitkAbstractNodeSelectionWidget() override;
/**
* @brief Sets the data storage that will be used / monitored by widget.
*
* @par dataStorage A pointer to the data storage to set.
*/
void SetDataStorage(mitk::DataStorage* dataStorage);
/**
* Sets the node predicate and updates the widget, according to the node predicate.
* Implement OnNodePredicateChange() for custom actualization of a derived widget class.
*
* @par nodePredicate A pointer to node predicate.
*/
void SetNodePredicate(const mitk::NodePredicateBase* nodePredicate);
const mitk::NodePredicateBase* GetNodePredicate() const;
QString GetInvalidInfo() const;
QString GetEmptyInfo() const;
QString GetPopUpTitel() const;
QString GetPopUpHint() const;
bool GetSelectionIsOptional() const;
bool GetSelectOnlyVisibleNodes() const;
using NodeList = QList<mitk::DataNode::Pointer>;
/** Other node container type often used in the code base.*/
using ConstNodeStdVector = std::vector<mitk::DataNode::ConstPointer>;
/** Returns the selected nodes, as emitted with CurrentSelectionChanged*/
NodeList GetSelectedNodes() const;
- /** Convinience method that returns the selected nodes as ConstNodeStdVector.
+ /** Convenience method that returns the selected nodes as ConstNodeStdVector.
This is a type also often used in the mitk code base.*/
ConstNodeStdVector GetSelectedNodesStdVector() const;
Q_SIGNALS:
/**
* @brief A signal that will be emitted if the selected node has changed.
*
* @par nodes A list of data nodes that are newly selected.
*/
void CurrentSelectionChanged(NodeList nodes);
public Q_SLOTS:
/**
* @brief Change the selection modus of the item view's selection model.
*
* If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view.
* An outgoing selection can then at most contain the filtered nodes.
* If false, the incoming non-visible selection will be stored and later added to the outgoing selection,
* to include the original selection that could not be modified.
* The part of the original selection, that is non-visible are the nodes, that do not fullfill the predicate.
*
* @par selectOnlyVisibleNodes The bool value to define the selection modus.
*/
void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes);
/**
* @brief Transform a list of data nodes (a selection) into a model selection and set this as a new selection of the
* selection model of the private member item view.
*
* The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If
* necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case
* the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable
* in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection
* but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes').
*
* @par nodes A list of data nodes that should be newly selected.
*/
void SetCurrentSelection(NodeList selectedNodes);
/** Set the info text that should be displayed if no (valid) node is selected,
* but a selection is mandatory.
* The string can contain HTML code, if desired.
*/
void SetInvalidInfo(QString info);
/** Set the info text that should be displayed if no (valid) node is selected,
* but a selection is optional.
* The string can contain HTML code, if desired.
*/
void SetEmptyInfo(QString info);
/** Set the caption of the popup that is displayed to alter the selection.
* The string can contain HTML code, if desired.
*/
void SetPopUpTitel(QString info);
/** Set the hint text of the popup that is displayed to alter the selection.
* The string can contain HTML code, if desired.
*/
void SetPopUpHint(QString info);
/** Set the widget into an optional mode. Optional means that the selection of no valid
* node does not mean an invalid state. Thus no node is a valid "node" selection too.
*/
void SetSelectionIsOptional(bool isOptional);
protected Q_SLOTS:
/** Call to remove a node from the current selection. If the node is part of the current selection,
* this will trigger ReviseSelectionChanged(), AllowEmissionOfSelection() and if there is really a change,
* will also emit CurrentSelectionChanged.
*/
void RemoveNodeFromSelection(const mitk::DataNode* node);
protected:
/** Method is called if the display of the selected nodes should be updated (e.g. because the selection changed). */
virtual void UpdateInfo() = 0;
/** Method is called if the predicate has changed, before the selection will be updated according to the new predicate.
* The default implementation does nothing.
* @remark If you are only interested to know when the selection has changed, overwrite OnInternalSelectionChange().
*/
virtual void OnNodePredicateChanged();
- /** Method is called if the data storage has changed. The selection will be automatically be reseted afterwards.
+ /** Method is called if the data storage has changed. The selection will be automatically be reset afterwards.
* The default implementation does nothing.
*/
virtual void OnDataStorageChanged();
/** This member function will called when ever a new internal selection has been determined. This can be
* used to update the state of internal widgets. The default implementation does nothing.
*/
virtual void OnInternalSelectionChanged();
/** Method is called when a node is added to the storage. Default implementation does nothing.
* Derived widgets can override the method if they want to react on new nodes in the storage.
*/
virtual void OnNodeAddedToStorage(const mitk::DataNode* node);
/** Method is called when a node is removed from the storage. The removed node is passed as
* variable. This member is called directly before the node will be removed from the current selection.
* Default implementation does nothing.
* Derived widgets can override the method if they want to handle to-be-removed nodes before.
*/
virtual void OnNodeRemovedFromStorage(const mitk::DataNode* node);
/** Method is called when a node is modified. The modified node is passed as 'caller' variable.
* Default implementation handles changes that are related to the node predicate:
* - If the node does not fit the node predicate anymore, it will be removed.
* - If the node was part of the external selection and now fits the node predicate,
* a new selection is compiled and emitted.
* Derived widgets can override the method if they want to react on modified nodes.
*/
virtual void OnNodeModified(const itk::Object* caller, const itk::EventObject& event);
- /** Method is called if the internal selection has changed. It will call following methods, that can be overriden to change
+ /** Method is called if the internal selection has changed. It will call following methods, that can be overridden to change
* behavior in derived classes:
* - pre internal selection change: ReviseSelectionChanged()
* - post internal selection change: OnInternalSelectionChanged(), UpdateInfo() and AllowEmissionOfSelection() (via EmitSelection()).
* If the emission is needed and allowed it will also trigger the emission via EmitSelection().
*/
void HandleChangeOfInternalSelection(NodeList newInternalSelection);
/** Compiles the list of node that would be emitted. It always contains the internal selection.
* Depending on SelectOnlyVisibleNodes it also adds all external select nodes that weren't visible (failed the predicate).
*/
NodeList CompileEmitSelection() const;
/** This member function is called if the internal selection is about to be changed by the base implementation.
* This is the slot where derived classes can revise and change the internal selection before widget updates,
* signal emissions and other things are triggered. Default implementation does nothing, thus it keeps the
* passed internal selection as compiled by the base implementation.
*/
virtual void ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection);
/** This function will be called before the CurrentSelectionChanged signal is emitted. The return value indicates
* if the signal should be emitted (true = emission; false = no emission). The default implementation always
* returns true.
* @param emissionCandidates The nodes that will be emitted if the function returns true.
*/
virtual bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const;
/** Checks if the new emission differs from the last emission. If this is the case and AllowEmissionOfSelection()
- * returns true the new selection will be emited.
+ * returns true the new selection will be emitted.
*/
void EmitSelection(const NodeList& emissionCandidates);
void SetCurrentInternalSelection(NodeList selectedNodes);
const NodeList& GetCurrentInternalSelection() const;
const NodeList& GetCurrentExternalSelection() const;
mitk::WeakPointer<mitk::DataStorage> m_DataStorage;
mitk::NodePredicateBase::ConstPointer m_NodePredicate;
QString m_InvalidInfo;
QString m_EmptyInfo;
QString m_PopUpTitel;
QString m_PopUpHint;
/** See documentation of SetSelectOnlyVisibleNodes for details*/
bool m_IsOptional;
/** See documentation of SetSelectionIsOptional for details*/
bool m_SelectOnlyVisibleNodes;
private:
/** Helper triggered on the storage delete event */
void SetDataStorageDeleted();
/**Member is called when a node is added to the storage.
Derived widgets can override the method OnNodeAddedToStorage if they want to react on new nodes in the storage.*/
void NodeAddedToStorage(const mitk::DataNode* node);
/**Member is called when a node is removed from the storage. It calls OnNodeRemovedFromStorage() and afterwards
it removes the removed node form the selection (if it is part of the current selection).
Derived classes can override OnNodeRemovedFromStorage() to react on the fact that a node might be removed and
their selection might change, because the removed node is part of there selection.*/
void NodeRemovedFromStorage(const mitk::DataNode* node);
void AddNodeObserver(mitk::DataNode* node);
void RemoveNodeObserver(mitk::DataNode* node);
unsigned long m_DataStorageDeletedTag;
NodeList m_CurrentInternalSelection;
NodeList m_CurrentExternalSelection;
NodeList m_LastEmission;
bool m_LastEmissionAllowance;
using NodeObserverTagMapType = std::map<const mitk::DataNode*, unsigned long>;
NodeObserverTagMapType m_NodeObserverTags;
/** Help to prevent recursions due to signal loops when emitting selections.*/
bool m_RecursionGuard;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.h b/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.h
index 39ca35f480..1fcfdd7429 100644
--- a/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.h
+++ b/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.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 QmitkDataStorageSimpleTreeModel_h
#define QmitkDataStorageSimpleTreeModel_h
#include <MitkQtWidgetsExports.h>
// qt widgets module
#include <QmitkAbstractDataStorageModel.h>
class QmitkDataStorageTreeModelInternalItem;
/**
* @brief The 'QmitkDataStorageSimpleTreeModel' is a basic tree model, derived from the 'QmitkAbstractDataStorageModel'.
* It provides functions to accept a data storage and a node predicate in order to customize the model data nodes.
* Furthermore it overrides the functions of 'QAbstractItemModel' to create a simple qt list model.*
* This model can be used in conjunction with a 'QmitkDataStorageSelectionConnector'.
* This model is a "light" version of the classic QmitkDataStorgageTreeModel. The differences between both are the following:
* - This class currently does not support DragNDrop.
* - This class does not have the ability to change hierarchy or changes the layer property of nodes.
* This was skipped on purpose, because that is not the job of the storage model.
* - If a tree item A is removed this class does not attach children of A to the parent of A.
* Instead the complete tree representation is updated. This was changed on purpose because otherwise the internal
* representation of the model would not reflect the data storage graph anymore.
*/
class MITKQTWIDGETS_EXPORT QmitkDataStorageSimpleTreeModel : public QmitkAbstractDataStorageModel
{
Q_OBJECT
public:
QmitkDataStorageSimpleTreeModel(QObject *parent);
~QmitkDataStorageSimpleTreeModel() override;
// override from 'QmitkAbstractDataStorageModel'
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void DataStorageChanged() override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodePredicateChanged() override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeAdded(const mitk::DataNode *node) override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeChanged(const mitk::DataNode *node) override;
/*
* @brief See 'QmitkAbstractDataStorageModel'
*/
void NodeRemoved(const mitk::DataNode *node) override;
// override pure virtual from 'QAbstractItemModel'
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
protected:
using TreeItem = QmitkDataStorageTreeModelInternalItem;
private:
void UpdateModelData();
void AddNodeInternal(const mitk::DataNode *node);
mitk::DataNode *GetParentNode(const mitk::DataNode *node) const;
TreeItem *TreeItemFromIndex(const QModelIndex &index) const;
QModelIndex IndexFromTreeItem(TreeItem *item) const;
void ResetTree();
TreeItem *m_Root;
/**helper structure to check, if a tree item is really part of the model.
- Prefered over iterating over the tree by hand because we can use std::find.*/
+ Preferred over iterating over the tree by hand because we can use std::find.*/
std::list<const TreeItem*> m_TreeItems;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h b/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h
index 5cb04c5b88..f68a9773d0 100644
--- a/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h
+++ b/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h
@@ -1,209 +1,209 @@
/*============================================================================
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 QmitkDataStorageTreeModel_h
#define QmitkDataStorageTreeModel_h
#include <MitkQtWidgetsExports.h>
#include <mitkDataStorage.h>
#include <mitkNodePredicateBase.h>
#include <mitkWeakPointer.h>
#include <QAbstractListModel>
#include "QmitkCustomVariants.h"
#include "QmitkEnums.h"
#include <QList>
#include <string>
#include <vector>
class QmitkDataStorageTreeModelInternalItem;
/** \ingroup QmitkModule
@warning This class causes invalid point exception when used with invalid QModelIndex instances.
The index validation is not sufficient. This may cause unspecific crashes in situation where
this class is used multiple times or with multiple selection models. See https://phabricator.mitk.org/T24348
for more information.
*/
class MITKQTWIDGETS_EXPORT QmitkDataStorageTreeModel : public QAbstractItemModel
{
Q_OBJECT
//# CONSTANTS,TYPEDEFS
public:
static const std::string COLUMN_NAME;
static const std::string COLUMN_TYPE;
static const std::string COLUMN_VISIBILITY;
//# CTORS,DTOR
public:
QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage, bool _PlaceNewNodesOnTop = false, QObject *parent = nullptr);
~QmitkDataStorageTreeModel() override;
//# GETTER
public:
///
/// Get node at a specific model index.
/// This function is used to get a node from a QModelIndex
///
mitk::DataNode::Pointer GetNode(const QModelIndex &index) const;
///
/// Returns a copy of the node-vector that is shown by this model
///
virtual QList<mitk::DataNode::Pointer> GetNodeSet() const;
///
/// Get the DataStorage.
///
const mitk::DataStorage::Pointer GetDataStorage() const;
///
/// Get the top placement flag
///
bool GetPlaceNewNodesOnTopFlag() { return m_PlaceNewNodesOnTop; }
///
/// Set the top placement flag
///
void SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop);
//# (Re-)implemented from QAbstractItemModel
//# Read model
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
//# hierarchical model
///
/// called whenever the model or the view needs to create a QModelIndex for a particular
/// child item (or a top-level item if parent is an invalid QModelIndex)
///
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
//# editable model
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override;
bool dropMimeData(
const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
static QMimeData *mimeDataFromModelIndexList(const QModelIndexList &indexes);
//# End of QAbstractItemModel
//# SETTER
public:
///
- /// Sets the DataStorage. The whole model will be resetted.
+ /// Sets the DataStorage. The whole model will be reset.
///
void SetDataStorage(mitk::DataStorage *_DataStorage);
///
- /// Notify that the DataStorage was deleted. The whole model will be resetted.
+ /// Notify that the DataStorage was deleted. The whole model will be reset.
///
void SetDataStorageDeleted();
///
/// Adds a node to this model.
/// If a predicate is set (not null) the node will be checked against it.The node has to have a data object (no one
/// wants to see empty nodes).
///
virtual void AddNode(const mitk::DataNode *node);
///
/// Removes a node from this model. Also removes any event listener from the node.
///
virtual void RemoveNode(const mitk::DataNode *node);
///
- /// Sets a node to modfified. Called by the DataStorage
+ /// Sets a node to modified. Called by the DataStorage
///
virtual void SetNodeModified(const mitk::DataNode *node);
///
/// \return an index for the given datatreenode in the tree. If the node is not found
///
QModelIndex GetIndex(const mitk::DataNode *) const;
/// Set whether to allow hierarchy changes by dragging and dropping
void SetAllowHierarchyChange(bool allowHierarchyChange);
signals:
void nodeVisibilityChanged();
//# MISC
protected:
using TreeItem = QmitkDataStorageTreeModelInternalItem;
QList<TreeItem *> ToTreeItemPtrList(const QMimeData *mimeData);
QList<TreeItem *> ToTreeItemPtrList(const QByteArray &ba);
///
/// Adjusts the LayerProperty according to the nodes position
///
void AdjustLayerProperty();
///
/// invoked after m_DataStorage or m_Predicate changed
///
TreeItem *TreeItemFromIndex(const QModelIndex &index) const;
///
/// Gives a ModelIndex for the Tree Item
///
QModelIndex IndexFromTreeItem(TreeItem *) const;
///
/// Returns the first element in the nodes sources list (if available) or 0
///
mitk::DataNode *GetParentNode(const mitk::DataNode *node) const;
///
/// Adds all Childs in parent to vec. Before a child is added the function is called recursively
///
void TreeToVector(TreeItem *parent, std::vector<TreeItem *> &vec) const;
///
/// Adds all Childs in parent to vec. Before a child is added the function is called recursively
///
void TreeToNodeSet(TreeItem *parent, QList<mitk::DataNode::Pointer> &vec) const;
///
/// Update Tree Model
///
void Update();
//# ATTRIBUTES
protected:
mitk::WeakPointer<mitk::DataStorage> m_DataStorage;
mitk::NodePredicateBase::Pointer m_Predicate;
bool m_PlaceNewNodesOnTop;
TreeItem *m_Root;
/// Flag to block the data storage events if nodes are added/removed by this class.
bool m_BlockDataStorageEvents;
/// This decides whether or not it is allowed to assign a different parent to a node
/// If it is false, it is not possible to change the hierarchy of nodes by dragging
/// and dropping.
/// If it is true, dragging nodes on another node will replace all of their parents
/// with that one.
bool m_AllowHierarchyChange;
private:
void AddNodeInternal(const mitk::DataNode *);
void RemoveNodeInternal(const mitk::DataNode *);
///
/// Checks if dicom properties patient name, study names and series name exists
///
bool DicomPropertiesExists(const mitk::DataNode &) const;
unsigned long m_DataStorageDeletedTag;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkNodeDescriptor.h b/Modules/QtWidgets/include/QmitkNodeDescriptor.h
index fcd58de33e..6b698323a5 100644
--- a/Modules/QtWidgets/include/QmitkNodeDescriptor.h
+++ b/Modules/QtWidgets/include/QmitkNodeDescriptor.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 QmitkNodeDescriptor_h
#define QmitkNodeDescriptor_h
#include <MitkQtWidgetsExports.h>
#include "mitkDataNode.h"
#include <QAction>
#include <QIcon>
#include <QList>
#include <QString>
#include <QWidgetAction>
#include <map>
#include <mitkNodePredicateBase.h>
/**
* \ingroup QmitkModule
* \brief <i>Decorator</i> class for mitk::DataNode.
*
* \sa QmitkNodeDescriptorManager
*/
class MITKQTWIDGETS_EXPORT QmitkNodeDescriptor : public QObject
{
Q_OBJECT
public:
///
/// Creates a new QmitkNodeDescriptor
///
QmitkNodeDescriptor(const QString &_ClassName,
const QString &_PathToIcon,
mitk::NodePredicateBase *_Predicate,
QObject *parent);
QmitkNodeDescriptor(const QString &_ClassName,
const QIcon &_Icon,
mitk::NodePredicateBase *_Predicate,
QObject *parent);
///
/// Deletes all actions
///
~QmitkNodeDescriptor() override;
///
/// Returns a name for this class of DataNodes (e.g. "Image", "Image Mask", etc.)
///
virtual QString GetNameOfClass() const;
///
/// Returns an Icon for this class of DataNodes
///
virtual QIcon GetIcon(const mitk::DataNode *node) const;
///
/// Returns an Icon for this class of DataNodes
///
virtual QAction *GetSeparator() const;
///
/// Check if this class describes the given node
///
virtual bool CheckNode(const mitk::DataNode *node) const;
///
/// Create and return an action with this descriptor as owner
///
virtual void AddAction(QAction *action, bool isBatchAction = true);
///
/// Remove and delete (!) an action
///
virtual void RemoveAction(QAction *_Action);
///
/// Get all actions associated with this class of nodes
///
virtual QList<QAction *> GetActions() const;
///
/// Get all actions for this descriptor class that can be executed on multiple nodes
- /// (no priot knowledge abpout the node is required)
+ /// (no prior knowledge about the node is required)
///
virtual QList<QAction *> GetBatchActions() const;
public slots:
/// Called when an action was destroyed
void ActionDestroyed(QObject *obj = nullptr);
protected:
QString m_ClassName;
QIcon m_Icon;
mitk::NodePredicateBase::Pointer m_Predicate;
QList<QAction *> m_Actions;
QList<QAction *> m_BatchActions;
QAction *m_Separator;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkNodeSelectionButton.h b/Modules/QtWidgets/include/QmitkNodeSelectionButton.h
index 111c63a34a..03f8a21320 100644
--- a/Modules/QtWidgets/include/QmitkNodeSelectionButton.h
+++ b/Modules/QtWidgets/include/QmitkNodeSelectionButton.h
@@ -1,71 +1,71 @@
/*============================================================================
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 QmitkNodeSelectionButton_h
#define QmitkNodeSelectionButton_h
#include <MitkQtWidgetsExports.h>
#include <mitkDataNode.h>
#include <QPushButton>
#include <QPixmap>
/**
* @class QmitkNodeSelectionButton
* @brief Button class that can be used to display information about a given node.
* If the given node is a nullptr the node info text will be shown.
* The node info can be formatted text (e.g. HTML code; like the tooltip text).
*/
class MITKQTWIDGETS_EXPORT QmitkNodeSelectionButton : public QPushButton
{
Q_OBJECT
public:
explicit QmitkNodeSelectionButton(QWidget *parent = nullptr);
~QmitkNodeSelectionButton() override;
const mitk::DataNode* GetSelectedNode() const;
bool GetSelectionIsOptional() const;
public Q_SLOTS:
virtual void SetSelectedNode(const mitk::DataNode* node);
virtual void SetNodeInfo(QString info);
/** Set the widget into an optional mode. Optional means that the selection of no valid
node does not mean an invalid state. Thus no node is a valid "node" selection too.
The state influences if the info text is handled as an information (optional) or a
- warning (optiona==false).*/
+ warning (optional==false).*/
void SetSelectionIsOptional(bool isOptional);
protected:
void paintEvent(QPaintEvent *p) override;
void changeEvent(QEvent *event) override;
void AddNodeObserver();
void RemoveNodeObserver();
void OnNodeModified(const itk::Object * /*caller*/, const itk::EventObject &);
mitk::DataNode::ConstPointer m_SelectedNode;
QString m_Info;
bool m_OutDatedThumbNail;
QPixmap m_ThumbNail;
itk::ModifiedTimeType m_DataMTime;
itk::ModifiedTimeType m_SelectionPropMTime;
bool m_IsOptional;
unsigned long m_NodeModifiedObserverTag;
bool m_NodeObserved;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkOverlayWidget.h b/Modules/QtWidgets/include/QmitkOverlayWidget.h
index ad63fa8e6d..0661d0576b 100644
--- a/Modules/QtWidgets/include/QmitkOverlayWidget.h
+++ b/Modules/QtWidgets/include/QmitkOverlayWidget.h
@@ -1,45 +1,45 @@
/*============================================================================
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 QmitkOverlayWidget_h
#define QmitkOverlayWidget_h
#include <QWidget>
#include <MitkQtWidgetsExports.h>
-/** Simple widget that can be used to achive overlays. The overlay will lie above its parent.
+/** Simple widget that can be used to achieve overlays. The overlay will lie above its parent.
* This implementation just renders a semi transparent black background. To add content to the
* overlay derive from this class.*/
class MITKQTWIDGETS_EXPORT QmitkOverlayWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool transparentForMouseEvents READ isTransparentForMouseEvents WRITE setTransparentForMouseEvents)
public:
explicit QmitkOverlayWidget(QWidget* parent = nullptr);
~QmitkOverlayWidget() override;
bool isTransparentForMouseEvents() const;
void setTransparentForMouseEvents(bool transparent = true);
protected:
bool event(QEvent* e) override;
bool eventFilter(QObject* watched, QEvent* event) override;
void paintEvent(QPaintEvent* event) override;
private:
void installEventFilterOnParent();
void removeEventFilterFromParent();
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkServiceListWidget.h b/Modules/QtWidgets/include/QmitkServiceListWidget.h
index ba70714b54..e9100010e8 100644
--- a/Modules/QtWidgets/include/QmitkServiceListWidget.h
+++ b/Modules/QtWidgets/include/QmitkServiceListWidget.h
@@ -1,288 +1,288 @@
/*============================================================================
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 QmitkServiceListWidget_h
#define QmitkServiceListWidget_h
#include "MitkQtWidgetsExports.h"
#include "ui_QmitkServiceListWidgetControls.h"
#include <vector>
// QT headers
#include <QListWidgetItem>
#include <QWidget>
// Microservices
#include "mitkServiceInterface.h"
#include "usModuleContext.h"
#include "usServiceEvent.h"
#include "usServiceReference.h"
/**
* \ingroup QmitkModule
*
* \brief This widget provides abstraction for the handling of MicroServices.
*
* Place one in your Plugin and set it to look for a certain interface.
* One can also specify a filter and / or a property to use for captioning of
* the services. It also offers functionality to signal
* ServiceEvents and to return the actual classes, so only a minimum of
* interaction with the MicroserviceInterface is required.
* To get started, just put it in your Plugin or Widget, call the Initialize
* Method and optionally connect it's signals.
* As QT limits templating possibilities, events only throw ServiceReferences.
* You can manually dereference them using TranslateServiceReference()
*/
class MITKQTWIDGETS_EXPORT QmitkServiceListWidget : public QWidget
{
// this is needed for all Qt objects that should have a MOC object (everything that derives from QObject)
Q_OBJECT
private:
us::ModuleContext *m_Context;
/** \brief a filter to further narrow down the list of results*/
std::string m_Filter;
/** \brief The name of the ServiceInterface that this class should list */
std::string m_Interface;
/** \brief The name of the ServiceProperty that will be displayed in the list to represent the service */
std::string m_NamingProperty;
/** \brief Determines if the first entry of the list should be selected automatically if no entry was selected before
* (default false). */
bool m_AutomaticallySelectFirstEntry;
public:
static const std::string VIEW_ID;
QmitkServiceListWidget(QWidget *p = nullptr, Qt::WindowFlags f1 = {});
~QmitkServiceListWidget() override;
/** \brief Set if the first entry of the list should be selected automatically if no entry was selected before. */
void SetAutomaticallySelectFirstEntry(bool automaticallySelectFirstEntry);
/** \brief This method is part of the widget an needs not to be called separately. */
virtual void CreateQtPartControl(QWidget *parent);
/** \brief This method is part of the widget an needs not to be called separately. (Creation of the connections of
* main and control widget.)*/
virtual void CreateConnections();
/**
* \brief Will return true, if a service is currently selected and false otherwise.
*
* Call this before requesting service references to avoid invalid ServiceReferences.
*/
bool GetIsServiceSelected();
/**
* \brief Returns the currently selected Service as a ServiceReference.
*
* If no Service is selected, the result will probably be a bad pointer. call GetIsServiceSelected()
* beforehand to avoid this
*/
us::ServiceReferenceU GetSelectedServiceReference();
/**
* @return Returns all service references that are displayed in this widget.
*/
std::vector<us::ServiceReferenceU> GetAllServiceReferences();
/**
* \brief Use this function to return the all listed services as a class directly.
*
* Make sure you pass the appropriate type, or else this call will fail.
* Usually, you will pass the class itself, not the SmartPointer, but the function returns a pointer.
*/
template <class T>
std::vector<T *> GetAllServices()
{
// if (this->m_Controls->m_ServiceList->currentRow()==-1) return nullptr;
std::vector<us::ServiceReferenceU> refs = GetAllServiceReferences();
std::vector<T *> result;
for (std::size_t i = 0; i < refs.size(); i++)
{
result.push_back(m_Context->GetService(us::ServiceReference<T>(refs[i])));
}
return result;
}
/**
* \brief Use this function to return the currently selected service as a class directly.
*
* Make sure you pass the appropriate type, or else this call will fail.
* Usually, you will pass the class itself, not the SmartPointer, but the function returns a pointer. Example:
* \verbatim mitk::USDevice::Pointer device = GetSelectedService<mitk::USDevice>(); \endverbatim
* @return Returns the current selected device. Returns nullptr if no device is selected.
*/
template <class T>
T *GetSelectedService()
{
if (this->m_Controls->m_ServiceList->currentRow() == -1)
return nullptr;
us::ServiceReferenceU ref = GetServiceForListItem(this->m_Controls->m_ServiceList->currentItem());
return (m_Context->GetService(us::ServiceReference<T>(ref)));
}
/**
* \brief Initializes the Widget with essential parameters.
*
* The string filter is an LDAP parsable String, compare mitk::ModuleContext for examples on filtering.
* Pass class T to tell the widget which class it should filter for - only services of this class will be listed.
* NamingProperty is a property that will be used to caption the Items in the list. If no filter is supplied, all
* matching interfaces are shown. If no namingProperty is supplied, the interfaceName will be used to caption Items in
the list.
* For example, this Initialization will filter for all USDevices that are set to active. The USDevice's model will be
used to display it in the list:
* \verbatim
std::string filter = "(&(" + us::ServiceConstants::OBJECTCLASS() + "=" +
"org.mitk.services.UltrasoundDevice)(IsActive=true))";
m_Controls.m_ActiveVideoDevices->Initialize<mitk::USDevice>(mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME
,filter);
* \endverbatim
*/
template <class T>
void Initialize(const std::string &namingProperty = static_cast<std::string>(""),
const std::string &filter = static_cast<std::string>(""))
{
std::string interfaceName(us_service_interface_iid<T>());
m_Interface = interfaceName;
InitPrivate(namingProperty, filter);
}
/**
* \brief Translates a serviceReference to a class of the given type.
*
* Use this to translate the signal's parameters. To adhere to the MicroService contract,
* only ServiceReferences stemming from the same widget should be used as parameters for this method.
* \verbatim mitk::USDevice::Pointer device = TranslateReference<mitk::USDevice>(myDeviceReference); \endverbatim
*/
template <class T>
T *TranslateReference(const us::ServiceReferenceU &reference)
{
return m_Context->GetService(us::ServiceReference<T>(reference));
}
/**
*\brief This Function listens to ServiceRegistry changes and updates the list of services accordingly.
*
- * The user of this widget does not need to call this method, it is instead used to recieve events from the module
+ * The user of this widget does not need to call this method, it is instead used to receive events from the module
*registry.
*/
void OnServiceEvent(const us::ServiceEvent event);
signals:
/**
*\brief Emitted when a new Service matching the filter is being registered.
*
* Be careful if you use a filter:
* If a device does not match the filter when registering, but modifies it's properties later to match the filter,
* then the first signal you will see this device in will be ServiceModified.
*/
void ServiceRegistered(us::ServiceReferenceU);
/**
*\brief Emitted directly before a Service matching the filter is being unregistered.
*/
void ServiceUnregistering(us::ServiceReferenceU);
/**
*\brief Emitted when a Service matching the filter changes it's properties, or when a service that formerly not
*matched the filter
* changed it's properties and now matches the filter.
*/
void ServiceModified(us::ServiceReferenceU);
/**
*\brief Emitted when a Service matching the filter changes it's properties,
*
* and the new properties make it fall trough the filter. This effectively means that
* the widget will not track the service anymore. Usually, the Service should still be useable though
*/
void ServiceModifiedEndMatch(us::ServiceReferenceU);
/**
*\brief Emitted if the user selects a Service from the list.
*
* If no service is selected, an invalid serviceReference is returned. The user can easily check for this.
* if (serviceReference) will evaluate to false, if the reference is invalid and true if valid.
*/
void ServiceSelectionChanged(us::ServiceReferenceU);
public slots:
protected slots:
/**
\brief Called, when the selection in the list of Services changes.
*/
void OnServiceSelectionChanged();
protected:
Ui::QmitkServiceListWidgetControls *m_Controls; ///< member holding the UI elements of this widget
/**
* \brief Internal structure used to link ServiceReferences to their QListWidgetItems
*/
struct ServiceListLink
{
us::ServiceReferenceU service;
QListWidgetItem *item;
};
/**
* \brief Finishes initialization after Initialize has been called.
*
* This function assumes that m_Interface is set correctly (Which Initialize does).
*/
void InitPrivate(const std::string &namingProperty, const std::string &filter);
/**
* \brief Contains a list of currently active services and their entires in the list. This is wiped with every
* ServiceRegistryEvent.
*/
std::vector<ServiceListLink> m_ListContent;
/**
* \brief Constructs a ListItem from the given service, displays it, and locally stores the service.
*/
QListWidgetItem *AddServiceToList(const us::ServiceReferenceU &serviceRef);
/**
* \brief Removes the given service from the list and cleans up. Returns true if successful, false if service was not
* found.
*/
bool RemoveServiceFromList(const us::ServiceReferenceU &serviceRef);
/**
* \brief Changes list entry of given service to match the changed service properties.
* \return true if successful, false if service was not found
*/
bool ChangeServiceOnList(const us::ServiceReferenceU &serviceRef);
/**
* \brief Returns the serviceReference corresponding to the given ListEntry or an invalid one if none was found (will
* evaluate to false in bool expressions).
*/
us::ServiceReferenceU GetServiceForListItem(QListWidgetItem *item);
/**
* \brief Returns a list of ServiceReferences matching the filter criteria by querying the service registry.
*/
std::vector<us::ServiceReferenceU> GetAllRegisteredServices();
/**
* \brief Gets string from the naming property of the service.
* \return caption string for given us::ServiceReferenceU
*/
QString CreateCaptionForService(const us::ServiceReferenceU &serviceRef);
};
#endif
diff --git a/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h b/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h
index f390d26c64..4765cc6467 100644
--- a/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h
+++ b/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h
@@ -1,73 +1,73 @@
/*============================================================================
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 mitkIDataStorageInspectorProvider_h
#define mitkIDataStorageInspectorProvider_h
#include <mitkServiceInterface.h>
#include <MitkQtWidgetsExports.h>
#include <QIcon>
class QmitkAbstractDataStorageInspector;
namespace mitk
{
/**
* \ingroup MicroServices_Interfaces
*
* \brief The common interface for all DataStorage inspector providers.
*
* Implementations of this interface must be registered as a service
* to make themselves available via the service registry.
*
* It is recommended to derive new implementations from QmitkDataStorageInspectorProviderBase
* which provide correct service registration semantics.
*
* \sa QmitkDataStorageInspectorProviderBase
*/
struct MITKQTWIDGETS_EXPORT IDataStorageInspectorProvider
{
virtual ~IDataStorageInspectorProvider();
/**
* \brief returns an inspector instance represented by the provider.
*/
virtual QmitkAbstractDataStorageInspector* CreateInspector() const = 0;
using InspectorIDType = std::string;
- /** Return the uniqe ID for the inspector type provided.*/
+ /** Return the unique ID for the inspector type provided.*/
virtual InspectorIDType GetInspectorID() const = 0;
/** Return the display name (e.g. used in the UI) for the inspector type provided.*/
virtual std::string GetInspectorDisplayName() const = 0;
/** Returns a description of the inspector type provided.*/
virtual std::string GetInspectorDescription() const = 0;
/** Returns the svg data of the icon of the inspector. Empty array indicates that no icon is defined.
@remark It is passed as svg file content and not as icon directly to allow later styling*/
virtual QIcon GetInspectorIcon() const = 0;
/**
* @brief Service property name for the inspector ID.
*
* The property value must be of type \c std::string.
*
* @return The property name.
*/
static std::string PROP_INSPECTOR_ID();
};
} // namespace mitk
MITK_DECLARE_SERVICE_INTERFACE(mitk::IDataStorageInspectorProvider, "org.mitk.IDataStorageInspectorProvider")
#endif
diff --git a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp
index bdb8eaa359..d0790137b6 100644
--- a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp
+++ b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp
@@ -1,509 +1,509 @@
/*============================================================================
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 "QmitkDataStorageTableModel.h"
//# Own includes
#include "QmitkCustomVariants.h"
#include "QmitkEnums.h"
#include "mitkNodePredicateBase.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include <QmitkNodeDescriptorManager.h>
//# Toolkit includes
#include <QFile>
#include <QIcon>
#include <itkCommand.h>
//#CTORS/DTOR
QmitkDataStorageTableModel::QmitkDataStorageTableModel(mitk::DataStorage::Pointer _DataStorage,
mitk::NodePredicateBase *_Predicate,
QObject *parent)
: QAbstractTableModel(parent),
m_DataStorage(nullptr),
m_Predicate(nullptr),
m_BlockEvents(false),
m_SortDescending(false)
{
this->SetPredicate(_Predicate);
this->SetDataStorage(_DataStorage);
}
QmitkDataStorageTableModel::~QmitkDataStorageTableModel()
{
// set data storage 0 to remove event listeners
this->SetDataStorage(nullptr);
}
//# Public GETTER
const mitk::DataStorage::Pointer QmitkDataStorageTableModel::GetDataStorage() const
{
return m_DataStorage.Lock();
}
mitk::NodePredicateBase::Pointer QmitkDataStorageTableModel::GetPredicate() const
{
return m_Predicate;
}
mitk::DataNode::Pointer QmitkDataStorageTableModel::GetNode(const QModelIndex &index) const
{
mitk::DataNode::Pointer node;
if (index.isValid())
{
node = m_NodeSet.at(index.row());
}
return node;
}
QVariant QmitkDataStorageTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant headerData;
// show only horizontal header
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal)
{
// first column: "Name"
if (section == 0)
headerData = "Name";
else if (section == 1)
headerData = "Data Type";
else if (section == 2)
headerData = "Visibility";
}
else if (orientation == Qt::Vertical)
{
// show numbers for rows
headerData = section + 1;
}
}
return headerData;
}
Qt::ItemFlags QmitkDataStorageTableModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
// name & visibility is editable
if (index.column() == 0)
{
flags |= Qt::ItemIsEditable;
}
else if (index.column() == 2)
{
flags |= Qt::ItemIsUserCheckable;
}
return flags;
}
int QmitkDataStorageTableModel::rowCount(const QModelIndex &) const
{
return m_NodeSet.size();
}
int QmitkDataStorageTableModel::columnCount(const QModelIndex &) const
{
- // show name, type and visible columnn
+ // show name, type and visible column
int columns = 3;
return columns;
}
QVariant QmitkDataStorageTableModel::data(const QModelIndex &index, int role) const
{
QVariant data;
if (index.isValid() && !m_NodeSet.empty())
{
mitk::DataNode::Pointer node = m_NodeSet.at(index.row());
std::string nodeName = node->GetName();
if (nodeName.empty())
nodeName = "unnamed";
// get name
if (index.column() == 0)
{
// get name of node (may also be edited)
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
data = QString::fromStdString(nodeName);
}
else if (role == QmitkDataNodeRole)
{
data = QVariant::fromValue(node);
}
}
else if (index.column() == 1)
{
QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(node);
// get type property of mitk::BaseData
if (role == Qt::DisplayRole)
{
data = nodeDescriptor->GetNameOfClass();
}
// show some nice icons for datatype
else if (role == Qt::DecorationRole)
{
data = nodeDescriptor->GetIcon(node);
}
}
else if (index.column() == 2)
{
// get visible property of mitk::BaseData
bool visibility = false;
if (node->GetVisibility(visibility, nullptr) && role == Qt::CheckStateRole)
{
data = (visibility ? Qt::Checked : Qt::Unchecked);
} // node->GetVisibility(visibility, 0) && role == Qt::CheckStateRole
} // index.column() == 2
} // index.isValid() && !m_NodeSet.empty()
return data;
}
//# Public SETTERS
void QmitkDataStorageTableModel::SetPredicate(mitk::NodePredicateBase *_Predicate)
{
// ensure that a new predicate is set in order to avoid unnecessary changed events
if (m_Predicate != _Predicate)
{
m_Predicate = _Predicate;
this->Reset();
}
}
void QmitkDataStorageTableModel::SetDataStorage(mitk::DataStorage::Pointer _DataStorage)
{
// only proceed if we have a new datastorage
if (m_DataStorage != _DataStorage)
{
auto dataStorage = m_DataStorage.Lock();
// if a data storage was set before remove old event listeners
if (dataStorage.IsNotNull())
{
dataStorage->AddNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::AddNode));
dataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::RemoveNode));
}
// set new data storage
m_DataStorage = _DataStorage;
dataStorage = m_DataStorage.Lock();
// if new storage is not 0 subscribe for events
if (dataStorage.IsNotNull())
{
// subscribe for node added/removed events
dataStorage->AddNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::AddNode));
dataStorage->RemoveNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::RemoveNode));
}
// Reset model (even if datastorage is 0->will be checked in Reset())
this->Reset();
}
}
void QmitkDataStorageTableModel::AddNode(const mitk::DataNode *node)
{
// garantuee no recursions when a new node event is thrown
if (!m_BlockEvents)
{
// if we have a predicate, check node against predicate first
if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(node))
return;
// dont add nodes without data (formerly known as helper objects)
if (node->GetData() == nullptr)
return;
// create listener commands to listen to changes in the name or the visibility of the node
itk::MemberCommand<QmitkDataStorageTableModel>::Pointer propertyModifiedCommand =
itk::MemberCommand<QmitkDataStorageTableModel>::New();
propertyModifiedCommand->SetCallbackFunction(this, &QmitkDataStorageTableModel::PropertyModified);
mitk::BaseProperty *tempProperty = nullptr;
// add listener for properties
tempProperty = node->GetProperty("visible");
if (tempProperty)
m_VisiblePropertyModifiedObserverTags[tempProperty] =
tempProperty->AddObserver(itk::ModifiedEvent(), propertyModifiedCommand);
tempProperty = node->GetProperty("name");
if (tempProperty)
m_NamePropertyModifiedObserverTags[tempProperty] =
tempProperty->AddObserver(itk::ModifiedEvent(), propertyModifiedCommand);
// emit beginInsertRows event
beginInsertRows(QModelIndex(), m_NodeSet.size(), m_NodeSet.size());
// add node
m_NodeSet.push_back(const_cast<mitk::DataNode *>(node));
// emit endInsertRows event
endInsertRows();
}
}
void QmitkDataStorageTableModel::RemoveNode(const mitk::DataNode *node)
{
// garantuee no recursions when a new node event is thrown
if (!m_BlockEvents)
{
// find corresponding node
auto nodeIt = std::find(m_NodeSet.begin(), m_NodeSet.end(), node);
if (nodeIt != m_NodeSet.end())
{
// now: remove listeners for name property ...
mitk::BaseProperty *tempProperty = nullptr;
tempProperty = (*nodeIt)->GetProperty("visible");
if (tempProperty)
tempProperty->RemoveObserver(m_VisiblePropertyModifiedObserverTags[tempProperty]);
m_VisiblePropertyModifiedObserverTags.erase(tempProperty);
// ... and visibility property
tempProperty = (*nodeIt)->GetProperty("name");
if (tempProperty)
tempProperty->RemoveObserver(m_NamePropertyModifiedObserverTags[tempProperty]);
m_NamePropertyModifiedObserverTags.erase(tempProperty);
// get an index from iterator
int row = std::distance(m_NodeSet.begin(), nodeIt);
// emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model)
this->beginRemoveRows(QModelIndex(), row, row);
// remove node
m_NodeSet.erase(nodeIt);
// emit endRemoveRows event
endRemoveRows();
}
}
}
void QmitkDataStorageTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject &)
{
if (!m_BlockEvents)
{
// get modified property
const mitk::BaseProperty *modifiedProperty = dynamic_cast<const mitk::BaseProperty *>(caller);
if (modifiedProperty)
{
// find node that holds the modified property
int row = -1;
int column = -1;
std::vector<mitk::DataNode *>::iterator it;
mitk::BaseProperty *visibilityProperty = nullptr;
mitk::BaseProperty *nameProperty = nullptr;
// search for property that changed and emit datachanged on the corresponding ModelIndex
for (it = m_NodeSet.begin(); it != m_NodeSet.end(); it++)
{
// check for the visible property or the name property
visibilityProperty = (*it)->GetProperty("visible");
if (modifiedProperty == visibilityProperty)
{
column = 2;
break;
}
nameProperty = (*it)->GetProperty("name");
if (modifiedProperty == nameProperty)
{
column = 0;
break;
}
}
// if we have the property we have a valid iterator
if (it != m_NodeSet.end())
row = std::distance(m_NodeSet.begin(), it);
// now emit the dataChanged signal
QModelIndex indexOfChangedProperty = index(row, column);
emit dataChanged(indexOfChangedProperty, indexOfChangedProperty);
}
}
}
bool QmitkDataStorageTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool noErr = false;
if (index.isValid() && (role == Qt::EditRole || role == Qt::CheckStateRole))
{
// any change events produced here should not be caught in this class
// --> set m_BlockEvents to true
m_BlockEvents = true;
mitk::DataNode::Pointer node = m_NodeSet.at(index.row());
if (index.column() == 0)
{
node->SetName(value.toString().toStdString());
}
else if (index.column() == 2)
{
node->SetBoolProperty("visible", (value.toInt() == Qt::Checked ? true : false));
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
// inform listeners about changes
emit dataChanged(index, index);
m_BlockEvents = false;
noErr = true;
}
return noErr;
}
//#Protected SETTER
void QmitkDataStorageTableModel::Reset()
{
mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet;
// remove all nodes now (dont use iterators because removing elements
// would invalidate the iterator)
// start at the last element: first in, last out
unsigned int i = m_NodeSet.size();
while (!m_NodeSet.empty())
{
--i;
this->RemoveNode(m_NodeSet.at(i));
}
// normally now everything should be empty->just to be sure
// erase all arrays again
m_NamePropertyModifiedObserverTags.clear();
m_VisiblePropertyModifiedObserverTags.clear();
m_NodeSet.clear();
auto dataStorage = m_DataStorage.Lock();
// the whole reset depends on the fact if a data storage is set or not
if (dataStorage.IsNotNull())
{
_NodeSet = m_Predicate.IsNotNull()
? dataStorage->GetSubset(m_Predicate)
: dataStorage->GetAll();
// finally add all nodes to the model
for (auto it = _NodeSet->begin(); it != _NodeSet->end(); it++)
{
// save node
this->AddNode(*it);
}
}
}
void QmitkDataStorageTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */)
{
bool sortDescending = (order == Qt::DescendingOrder) ? true : false;
// do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!)
/*
if(sortDescending != m_SortDescending)
{*/
// m_SortDescending = sortDescending;
DataNodeCompareFunction::CompareCriteria _CompareCriteria = DataNodeCompareFunction::CompareByName;
DataNodeCompareFunction::CompareOperator _CompareOperator =
sortDescending ? DataNodeCompareFunction::Greater : DataNodeCompareFunction::Less;
if (column == 1)
_CompareCriteria = DataNodeCompareFunction::CompareByClassName;
else if (column == 2)
_CompareCriteria = DataNodeCompareFunction::CompareByVisibility;
DataNodeCompareFunction compareFunc(_CompareCriteria, _CompareOperator);
std::sort(m_NodeSet.begin(), m_NodeSet.end(), compareFunc);
QAbstractTableModel::beginResetModel();
QAbstractTableModel::endResetModel();
//}
}
std::vector<mitk::DataNode *> QmitkDataStorageTableModel::GetNodeSet() const
{
return m_NodeSet;
}
QmitkDataStorageTableModel::DataNodeCompareFunction::DataNodeCompareFunction(CompareCriteria _CompareCriteria,
CompareOperator _CompareOperator)
: m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator)
{
}
bool QmitkDataStorageTableModel::DataNodeCompareFunction::operator()(const mitk::DataNode::Pointer &_Left,
const mitk::DataNode::Pointer &_Right) const
{
switch (m_CompareCriteria)
{
case CompareByClassName:
if (m_CompareOperator == Less)
return (_Left->GetData()->GetNameOfClass() < _Right->GetData()->GetNameOfClass());
else
return (_Left->GetData()->GetNameOfClass() > _Right->GetData()->GetNameOfClass());
break;
case CompareByVisibility:
{
bool _LeftVisibility = false;
bool _RightVisibility = false;
_Left->GetVisibility(_LeftVisibility, nullptr);
_Right->GetVisibility(_RightVisibility, nullptr);
if (m_CompareOperator == Less)
return (_LeftVisibility < _RightVisibility);
else
return (_LeftVisibility > _RightVisibility);
}
break;
// CompareByName:
default:
if (m_CompareOperator == Less)
return (_Left->GetName() < _Right->GetName());
else
return (_Left->GetName() > _Right->GetName());
break;
}
}
diff --git a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp
index b6256986a4..c3231eebcd 100644
--- a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp
+++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp
@@ -1,883 +1,883 @@
/*============================================================================
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 <mitkNodePredicateAnd.h>
#include <mitkNodePredicateData.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateOr.h>
#include <mitkNodePredicateProperty.h>
#include <mitkPlanarFigure.h>
#include <mitkProperties.h>
#include <mitkRenderingManager.h>
#include <mitkStringProperty.h>
#include <mitkPropertyNameHelper.h>
#include "QmitkDataStorageTreeModel.h"
#include "QmitkDataStorageTreeModelInternalItem.h"
#include "QmitkNodeDescriptorManager.h"
#include <QmitkCustomVariants.h>
#include <QmitkEnums.h>
#include <QmitkMimeTypes.h>
#include <QFile>
#include <QIcon>
#include <QMimeData>
#include <QTextStream>
#include <map>
#include <mitkCoreServices.h>
QmitkDataStorageTreeModel::QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage,
bool _PlaceNewNodesOnTop,
QObject *parent)
: QAbstractItemModel(parent),
m_DataStorage(nullptr),
m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop),
m_Root(nullptr),
m_BlockDataStorageEvents(false),
m_AllowHierarchyChange(false)
{
this->SetDataStorage(_DataStorage);
}
QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel()
{
// set data storage to 0 = remove all listeners
this->SetDataStorage(nullptr);
m_Root->Delete();
m_Root = nullptr;
}
mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode(const QModelIndex &index) const
{
return this->TreeItemFromIndex(index)->GetDataNode();
}
const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const
{
return m_DataStorage.Lock();
}
QModelIndex QmitkDataStorageTreeModel::index(int row, int column, const QModelIndex &parent) const
{
TreeItem *parentItem;
if (!parent.isValid())
parentItem = m_Root;
else
parentItem = static_cast<TreeItem *>(parent.internalPointer());
TreeItem *childItem = parentItem->GetChild(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
int QmitkDataStorageTreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentTreeItem = this->TreeItemFromIndex(parent);
return parentTreeItem->GetChildCount();
}
Qt::ItemFlags QmitkDataStorageTreeModel::flags(const QModelIndex &index) const
{
if (index.isValid())
{
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable |
Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
else
{
return Qt::ItemIsDropEnabled;
}
}
int QmitkDataStorageTreeModel::columnCount(const QModelIndex & /* parent = QModelIndex() */) const
{
return 1;
}
QModelIndex QmitkDataStorageTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = this->TreeItemFromIndex(index);
TreeItem *parentItem = childItem->GetParent();
if (parentItem == m_Root)
return QModelIndex();
return this->createIndex(parentItem->GetIndex(), 0, parentItem);
}
QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItemFromIndex(const QModelIndex &index) const
{
if (index.isValid())
return static_cast<TreeItem *>(index.internalPointer());
else
return m_Root;
}
Qt::DropActions QmitkDataStorageTreeModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions QmitkDataStorageTreeModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
bool QmitkDataStorageTreeModel::dropMimeData(
const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent)
{
// Early exit, returning true, but not actually doing anything (ignoring data).
if (action == Qt::IgnoreAction)
{
return true;
}
// Note, we are returning true if we handled it, and false otherwise
bool returnValue = false;
if (data->hasFormat("application/x-qabstractitemmodeldatalist"))
{
returnValue = true;
// First we extract a Qlist of TreeItem* pointers.
QList<TreeItem *> listOfItemsToDrop = ToTreeItemPtrList(data);
if (listOfItemsToDrop.empty())
{
return false;
}
// Retrieve the TreeItem* where we are dropping stuff, and its parent.
TreeItem *dropItem = this->TreeItemFromIndex(parent);
TreeItem *parentItem = dropItem->GetParent();
// If item was dropped onto empty space, we select the root node
if (dropItem == m_Root)
{
parentItem = m_Root;
}
// Dragging and Dropping is only allowed within the same parent, so use the first item in list to validate.
// (otherwise, you could have a derived image such as a segmentation, and assign it to another image).
// NOTE: We are assuming the input list is valid... i.e. when it was dragged, all the items had the same parent.
// Determine whether or not the drag and drop operation is a valid one.
// Examples of invalid operations include:
// - dragging nodes with different parents
// - dragging nodes from one parent to another parent, if m_AllowHierarchyChange is false
// - dragging a node on one of its child nodes (only relevant if m_AllowHierarchyChange is true)
bool isValidDragAndDropOperation(true);
// different parents
{
TreeItem *firstParent = listOfItemsToDrop[0]->GetParent();
QList<TreeItem *>::iterator diIter;
for (diIter = listOfItemsToDrop.begin() + 1; diIter != listOfItemsToDrop.end(); diIter++)
{
if (firstParent != (*diIter)->GetParent())
{
isValidDragAndDropOperation = false;
break;
}
}
}
// dragging from one parent to another
if ((!m_AllowHierarchyChange) && isValidDragAndDropOperation)
{
if (row == -1) // drag onto a node
{
isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == parentItem;
}
else // drag between nodes
{
isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == dropItem;
}
}
// dragging on a child node of one the dragged nodes
{
QList<TreeItem *>::iterator diIter;
for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++)
{
TreeItem *tempItem = dropItem;
while (tempItem != m_Root)
{
tempItem = tempItem->GetParent();
if (tempItem == *diIter)
{
isValidDragAndDropOperation = false;
}
}
}
}
if (!isValidDragAndDropOperation)
return isValidDragAndDropOperation;
if (listOfItemsToDrop[0] != dropItem && isValidDragAndDropOperation)
{
// Retrieve the index of where we are dropping stuff.
QModelIndex parentModelIndex = this->IndexFromTreeItem(parentItem);
int dragIndex = 0;
// Iterate through the list of TreeItem (which may be at non-consecutive indexes).
QList<TreeItem *>::iterator diIter;
for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++)
{
TreeItem *itemToDrop = *diIter;
// if the item is dragged down we have to compensate its final position for the
// fact it is deleted lateron, this only applies if it is dragged within the same level
if ((itemToDrop->GetIndex() < row) && (itemToDrop->GetParent() == dropItem))
{
dragIndex = 1;
}
// Here we assume that as you remove items, one at a time, that GetIndex() will be valid.
this->beginRemoveRows(
this->IndexFromTreeItem(itemToDrop->GetParent()), itemToDrop->GetIndex(), itemToDrop->GetIndex());
itemToDrop->GetParent()->RemoveChild(itemToDrop);
this->endRemoveRows();
}
// row = -1 dropped on an item, row != -1 dropped in between two items
// Select the target index position, or put it at the end of the list.
int dropIndex = 0;
if (row != -1)
{
if (dragIndex == 0)
dropIndex = std::min(row, parentItem->GetChildCount() - 1);
else
dropIndex = std::min(row - 1, parentItem->GetChildCount() - 1);
}
else
{
dropIndex = dropItem->GetIndex();
}
QModelIndex dropItemModelIndex = this->IndexFromTreeItem(dropItem);
if ((row == -1 && dropItemModelIndex.row() == -1) || dropItemModelIndex.row() > parentItem->GetChildCount())
dropIndex = parentItem->GetChildCount() - 1;
// Now insert items again at the drop item position
if (m_AllowHierarchyChange)
{
this->beginInsertRows(dropItemModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1);
}
else
{
this->beginInsertRows(parentModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1);
}
for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++)
{
// dropped on node, behaviour depends on preference setting
if (m_AllowHierarchyChange)
{
auto dataStorage = m_DataStorage.Lock();
m_BlockDataStorageEvents = true;
mitk::DataNode::Pointer droppedNode = (*diIter)->GetDataNode();
mitk::DataNode *dropOntoNode = dropItem->GetDataNode();
dataStorage->Remove(droppedNode);
dataStorage->Add(droppedNode, dropOntoNode);
m_BlockDataStorageEvents = false;
dropItem->InsertChild((*diIter), dropIndex);
}
else
{
if (row == -1) // drag onto a node
{
parentItem->InsertChild((*diIter), dropIndex);
}
else // drag between nodes
{
dropItem->InsertChild((*diIter), dropIndex);
}
}
dropIndex++;
}
this->endInsertRows();
// Change Layers to match.
this->AdjustLayerProperty();
}
}
else if (data->hasFormat("application/x-mitk-datanodes"))
{
returnValue = true;
int numberOfNodesDropped = 0;
QList<mitk::DataNode *> dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data);
mitk::DataNode *node = nullptr;
foreach (node, dataNodeList)
{
auto datastorage = m_DataStorage.Lock();
if (node && datastorage.IsNotNull() && !datastorage->Exists(node))
{
m_DataStorage.Lock()->Add(node);
mitk::BaseData::Pointer basedata = node->GetData();
if (basedata.IsNotNull())
{
mitk::RenderingManager::GetInstance()->InitializeViews(basedata->GetTimeGeometry());
numberOfNodesDropped++;
}
}
}
// Only do a rendering update, if we actually dropped anything.
if (numberOfNodesDropped > 0)
{
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
return returnValue;
}
QStringList QmitkDataStorageTreeModel::mimeTypes() const
{
QStringList types = QAbstractItemModel::mimeTypes();
types << "application/x-qabstractitemmodeldatalist";
types << "application/x-mitk-datanodes";
return types;
}
QMimeData *QmitkDataStorageTreeModel::mimeData(const QModelIndexList &indexes) const
{
return mimeDataFromModelIndexList(indexes);
}
QMimeData *QmitkDataStorageTreeModel::mimeDataFromModelIndexList(const QModelIndexList &indexes)
{
QMimeData *ret = new QMimeData;
QString treeItemAddresses("");
QString dataNodeAddresses("");
QByteArray baTreeItemPtrs;
QByteArray baDataNodePtrs;
QDataStream dsTreeItemPtrs(&baTreeItemPtrs, QIODevice::WriteOnly);
QDataStream dsDataNodePtrs(&baDataNodePtrs, QIODevice::WriteOnly);
for (int i = 0; i < indexes.size(); i++)
{
TreeItem *treeItem = static_cast<TreeItem *>(indexes.at(i).internalPointer());
dsTreeItemPtrs << reinterpret_cast<quintptr>(treeItem);
dsDataNodePtrs << reinterpret_cast<quintptr>(treeItem->GetDataNode().GetPointer());
// --------------- deprecated -----------------
unsigned long long treeItemAddress = reinterpret_cast<unsigned long long>(treeItem);
unsigned long long dataNodeAddress = reinterpret_cast<unsigned long long>(treeItem->GetDataNode().GetPointer());
QTextStream(&treeItemAddresses) << treeItemAddress;
QTextStream(&dataNodeAddresses) << dataNodeAddress;
if (i != indexes.size() - 1)
{
QTextStream(&treeItemAddresses) << ",";
QTextStream(&dataNodeAddresses) << ",";
}
// -------------- end deprecated -------------
}
// ------------------ deprecated -----------------
ret->setData("application/x-qabstractitemmodeldatalist", QByteArray(treeItemAddresses.toLatin1()));
ret->setData("application/x-mitk-datanodes", QByteArray(dataNodeAddresses.toLatin1()));
// --------------- end deprecated -----------------
ret->setData(QmitkMimeTypes::DataStorageTreeItemPtrs, baTreeItemPtrs);
ret->setData(QmitkMimeTypes::DataNodePtrs, baDataNodePtrs);
return ret;
}
QVariant QmitkDataStorageTreeModel::data(const QModelIndex &index, int role) const
{
mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode();
// get name of treeItem (may also be edited)
QString nodeName = QString::fromStdString(dataNode->GetName());
if (nodeName.isEmpty())
{
nodeName = "unnamed";
}
if (role == Qt::DisplayRole)
return nodeName;
else if (role == Qt::ToolTipRole)
return nodeName;
else if (role == Qt::DecorationRole)
{
QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode);
return nodeDescriptor->GetIcon(dataNode);
}
else if (role == Qt::CheckStateRole)
{
return dataNode->IsVisible(nullptr);
}
else if (role == QmitkDataNodeRole)
{
return QVariant::fromValue<mitk::DataNode::Pointer>(mitk::DataNode::Pointer(dataNode));
}
else if (role == QmitkDataNodeRawPointerRole)
{
return QVariant::fromValue<mitk::DataNode *>(dataNode);
}
return QVariant();
}
bool QmitkDataStorageTreeModel::DicomPropertiesExists(const mitk::DataNode &node) const
{
bool propertiesExists = false;
mitk::BaseProperty *seriesDescription_deprecated = (node.GetProperty("dicom.series.SeriesDescription"));
mitk::BaseProperty *studyDescription_deprecated = (node.GetProperty("dicom.study.StudyDescription"));
mitk::BaseProperty *patientsName_deprecated = (node.GetProperty("dicom.patient.PatientsName"));
mitk::BaseProperty *seriesDescription =
(node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str()));
mitk::BaseProperty *studyDescription =
(node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str()));
mitk::BaseProperty *patientsName = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str()));
if (patientsName != nullptr && studyDescription != nullptr && seriesDescription != nullptr)
{
if ((!patientsName->GetValueAsString().empty()) && (!studyDescription->GetValueAsString().empty()) &&
(!seriesDescription->GetValueAsString().empty()))
{
propertiesExists = true;
}
}
- /** Code coveres the deprecated property naming for backwards compatibility */
+ /** Code covers the deprecated property naming for backwards compatibility */
if (patientsName_deprecated != nullptr && studyDescription_deprecated != nullptr && seriesDescription_deprecated != nullptr)
{
if ((!patientsName_deprecated->GetValueAsString().empty()) &&
(!studyDescription_deprecated->GetValueAsString().empty()) &&
(!seriesDescription_deprecated->GetValueAsString().empty()))
{
propertiesExists = true;
}
}
return propertiesExists;
}
QVariant QmitkDataStorageTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root)
return QString::fromStdString(m_Root->GetDataNode()->GetName());
return QVariant();
}
void QmitkDataStorageTreeModel::SetDataStorage(mitk::DataStorage *_DataStorage)
{
if (m_DataStorage != _DataStorage) // dont take the same again
{
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNotNull())
{
// remove Listener for the data storage itself
dataStorage->RemoveObserver(m_DataStorageDeletedTag);
// remove listeners for the nodes
dataStorage->AddNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(this,
&QmitkDataStorageTreeModel::AddNode));
dataStorage->ChangedNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::SetNodeModified));
dataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::RemoveNode));
}
this->beginResetModel();
// take over the new data storage
m_DataStorage = _DataStorage;
// delete the old root (if necessary, create new)
if (m_Root)
m_Root->Delete();
mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New();
rootDataNode->SetName("Data Manager");
m_Root = new TreeItem(rootDataNode, nullptr);
dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNotNull())
{
// add Listener for the data storage itself
auto command = itk::SimpleMemberCommand<QmitkDataStorageTreeModel>::New();
command->SetCallbackFunction(this, &QmitkDataStorageTreeModel::SetDataStorageDeleted);
m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command);
// add listeners for the nodes
dataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::AddNode));
dataStorage->ChangedNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::SetNodeModified));
dataStorage->RemoveNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::RemoveNode));
// finally add all nodes to the model
this->Update();
}
this->endResetModel();
}
}
void QmitkDataStorageTreeModel::SetDataStorageDeleted()
{
this->SetDataStorage(nullptr);
}
void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node)
{
auto dataStorage = m_DataStorage.Lock();
if (node == nullptr || dataStorage.IsNull() || !dataStorage->Exists(node) || m_Root->Find(node) != nullptr)
return;
// find out if we have a root node
TreeItem *parentTreeItem = m_Root;
QModelIndex index;
mitk::DataNode *parentDataNode = this->GetParentNode(node);
if (parentDataNode) // no top level data node
{
parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item
if (!parentTreeItem)
{
this->AddNode(parentDataNode);
parentTreeItem = m_Root->Find(parentDataNode);
if (!parentTreeItem)
return;
}
// get the index of this parent with the help of the grand parent
index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem);
}
// add node
if (m_PlaceNewNodesOnTop)
{
// emit beginInsertRows event
beginInsertRows(index, 0, 0);
parentTreeItem->InsertChild(new TreeItem(const_cast<mitk::DataNode *>(node)), 0);
}
else
{
int firstRowWithASiblingBelow = 0;
int nodeLayer = -1;
node->GetIntProperty("layer", nodeLayer);
for (TreeItem* siblingTreeItem: parentTreeItem->GetChildren())
{
int siblingLayer = -1;
if (mitk::DataNode* siblingNode = siblingTreeItem->GetDataNode())
{
siblingNode->GetIntProperty("layer", siblingLayer);
}
if (nodeLayer > siblingLayer)
{
break;
}
++firstRowWithASiblingBelow;
}
beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow);
parentTreeItem->InsertChild(new TreeItem(const_cast<mitk::DataNode*>(node)), firstRowWithASiblingBelow);
}
// emit endInsertRows event
endInsertRows();
if(m_PlaceNewNodesOnTop)
{
this->AdjustLayerProperty();
}
}
void QmitkDataStorageTreeModel::AddNode(const mitk::DataNode *node)
{
auto dataStorage = m_DataStorage.Lock();
if (node == nullptr || m_BlockDataStorageEvents || dataStorage.IsNull() || !dataStorage->Exists(node) ||
m_Root->Find(node) != nullptr)
return;
this->AddNodeInternal(node);
}
void QmitkDataStorageTreeModel::SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop)
{
m_PlaceNewNodesOnTop = _PlaceNewNodesOnTop;
}
void QmitkDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode *node)
{
if (!m_Root)
return;
TreeItem *treeItem = m_Root->Find(node);
if (!treeItem)
return; // return because there is no treeitem containing this node
TreeItem *parentTreeItem = treeItem->GetParent();
QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem);
// emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model)
this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex());
// remove node
std::vector<TreeItem*> children = treeItem->GetChildren();
delete treeItem;
// emit endRemoveRows event
endRemoveRows();
// move all children of deleted node into its parent
for (std::vector<TreeItem*>::iterator it = children.begin(); it != children.end(); it++)
{
// emit beginInsertRows event
beginInsertRows(parentIndex, parentTreeItem->GetChildCount(), parentTreeItem->GetChildCount());
// add nodes again
parentTreeItem->AddChild(*it);
// emit endInsertRows event
endInsertRows();
}
this->AdjustLayerProperty();
}
void QmitkDataStorageTreeModel::RemoveNode(const mitk::DataNode *node)
{
if (node == nullptr || m_BlockDataStorageEvents)
return;
this->RemoveNodeInternal(node);
}
void QmitkDataStorageTreeModel::SetNodeModified(const mitk::DataNode *node)
{
TreeItem *treeItem = m_Root->Find(node);
if (treeItem)
{
TreeItem *parentTreeItem = treeItem->GetParent();
// as the root node should not be removed one should always have a parent item
if (!parentTreeItem)
return;
QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem);
// now emit the dataChanged signal
emit dataChanged(index, index);
}
}
mitk::DataNode *QmitkDataStorageTreeModel::GetParentNode(const mitk::DataNode *node) const
{
mitk::DataNode *dataNode = nullptr;
mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage.Lock()->GetSources(node);
if (_Sources->Size() > 0)
dataNode = _Sources->front();
return dataNode;
}
bool QmitkDataStorageTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode();
if (!dataNode)
return false;
if (role == Qt::EditRole && !value.toString().isEmpty())
{
dataNode->SetName(value.toString().toStdString());
mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(dataNode->GetData());
if (planarFigure != nullptr)
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (role == Qt::CheckStateRole)
{
// Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element.
// Therefore the checkstate is being estimated again here.
QVariant qcheckstate = index.data(Qt::CheckStateRole);
int checkstate = qcheckstate.toInt();
bool isVisible = bool(checkstate);
dataNode->SetVisibility(!isVisible);
emit nodeVisibilityChanged();
}
// inform listeners about changes
emit dataChanged(index, index);
return true;
}
bool QmitkDataStorageTreeModel::setHeaderData(int /*section*/,
Qt::Orientation /*orientation*/,
const QVariant & /* value */,
int /*role = Qt::EditRole*/)
{
return false;
}
void QmitkDataStorageTreeModel::AdjustLayerProperty()
{
/// transform the tree into an array and set the layer property descending
std::vector<TreeItem *> vec;
this->TreeToVector(m_Root, vec);
int i = vec.size() - 1;
for (std::vector<TreeItem *>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
mitk::DataNode::Pointer dataNode = (*it)->GetDataNode();
bool fixedLayer = false;
if (!(dataNode->GetBoolProperty("fixedLayer", fixedLayer) && fixedLayer))
dataNode->SetIntProperty("layer", i);
--i;
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkDataStorageTreeModel::TreeToVector(TreeItem *parent, std::vector<TreeItem *> &vec) const
{
TreeItem *current;
for (int i = 0; i < parent->GetChildCount(); ++i)
{
current = parent->GetChild(i);
this->TreeToVector(current, vec);
vec.push_back(current);
}
}
QModelIndex QmitkDataStorageTreeModel::IndexFromTreeItem(TreeItem *item) const
{
if (item == m_Root)
return QModelIndex();
else
return this->createIndex(item->GetIndex(), 0, item);
}
QList<mitk::DataNode::Pointer> QmitkDataStorageTreeModel::GetNodeSet() const
{
QList<mitk::DataNode::Pointer> res;
if (m_Root)
this->TreeToNodeSet(m_Root, res);
return res;
}
void QmitkDataStorageTreeModel::TreeToNodeSet(TreeItem *parent, QList<mitk::DataNode::Pointer> &vec) const
{
TreeItem *current;
for (int i = 0; i < parent->GetChildCount(); ++i)
{
current = parent->GetChild(i);
vec.push_back(current->GetDataNode());
this->TreeToNodeSet(current, vec);
}
}
QModelIndex QmitkDataStorageTreeModel::GetIndex(const mitk::DataNode *node) const
{
if (m_Root)
{
TreeItem *item = m_Root->Find(node);
if (item)
return this->IndexFromTreeItem(item);
}
return QModelIndex();
}
QList<QmitkDataStorageTreeModel::TreeItem *> QmitkDataStorageTreeModel::ToTreeItemPtrList(const QMimeData *mimeData)
{
if (mimeData == nullptr || !mimeData->hasFormat(QmitkMimeTypes::DataStorageTreeItemPtrs))
{
return QList<TreeItem *>();
}
return ToTreeItemPtrList(mimeData->data(QmitkMimeTypes::DataStorageTreeItemPtrs));
}
QList<QmitkDataStorageTreeModel::TreeItem *> QmitkDataStorageTreeModel::ToTreeItemPtrList(const QByteArray &ba)
{
QList<TreeItem *> result;
QDataStream ds(ba);
while (!ds.atEnd())
{
quintptr treeItemPtr;
ds >> treeItemPtr;
result.push_back(reinterpret_cast<TreeItem *>(treeItemPtr));
}
return result;
}
void QmitkDataStorageTreeModel::Update()
{
auto datastorage = m_DataStorage.Lock();
if (datastorage.IsNotNull())
{
mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = datastorage->GetAll();
/// Regardless the value of this preference, the new nodes must not be inserted
/// at the top now, but at the position according to their layer.
bool newNodesWereToBePlacedOnTop = m_PlaceNewNodesOnTop;
m_PlaceNewNodesOnTop = false;
for (const auto& node : *_NodeSet)
{
this->AddNodeInternal(node);
}
m_PlaceNewNodesOnTop = newNodesWereToBePlacedOnTop;
/// Adjust the layers to ensure that derived nodes are above their sources.
this->AdjustLayerProperty();
}
}
void QmitkDataStorageTreeModel::SetAllowHierarchyChange(bool allowHierarchyChange)
{
m_AllowHierarchyChange = allowHierarchyChange;
}
diff --git a/Modules/QtWidgets/src/QmitkIOUtil.cpp b/Modules/QtWidgets/src/QmitkIOUtil.cpp
index e8f59fc640..0e9e4a8418 100644
--- a/Modules/QtWidgets/src/QmitkIOUtil.cpp
+++ b/Modules/QtWidgets/src/QmitkIOUtil.cpp
@@ -1,587 +1,587 @@
/*============================================================================
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 "QmitkIOUtil.h"
#include "mitkCoreServices.h"
#include "mitkCustomMimeType.h"
#include "mitkFileReaderRegistry.h"
#include "mitkFileWriterRegistry.h"
#include "mitkIMimeTypeProvider.h"
#include "mitkMimeType.h"
#include <mitkCoreObjectFactory.h>
#include <mitkIOUtil.h>
#include "QmitkFileReaderOptionsDialog.h"
#include "QmitkFileWriterOptionsDialog.h"
// QT
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QSet>
#include <QString>
#include <QStringList>
// ITK
#include <itksys/SystemTools.hxx>
#include <algorithm>
struct QmitkIOUtil::Impl
{
struct ReaderOptionsDialogFunctor : public ReaderOptionsFunctorBase
{
bool operator()(LoadInfo &loadInfo) const override
{
QmitkFileReaderOptionsDialog dialog(loadInfo);
if (dialog.exec() == QDialog::Accepted)
{
return !dialog.ReuseOptions();
}
else
{
loadInfo.m_Cancel = true;
return true;
}
}
};
struct WriterOptionsDialogFunctor : public WriterOptionsFunctorBase
{
bool operator()(SaveInfo &saveInfo) const override
{
QmitkFileWriterOptionsDialog dialog(saveInfo);
if (dialog.exec() == QDialog::Accepted)
{
return !dialog.ReuseOptions();
}
else
{
saveInfo.m_Cancel = true;
return true;
}
}
};
//! Filename characters that are not valid - depending on the platform (Windows, Posix)
static QString s_InvalidFilenameCharacters;
//! Return 'true' when:
//! - the specified filename contains characters not accepted by the file system (see s_InvalidFilenameCharacters)
//! - filename starts or ends in space characters
//!
//! This test is not exhaustive but just excluding the most common problems.
static bool IsIllegalFilename(const QString &fullFilename)
{
QFileInfo fi(fullFilename);
auto filename = fi.fileName();
for (const auto &ch : std::as_const(s_InvalidFilenameCharacters))
{
if (filename.contains(ch))
{
return true;
}
}
if (filename.startsWith(' ') || filename.endsWith(' '))
{
return true;
}
return false;
}
}; // Impl
#if defined(_WIN32) || defined(_WIN64)
QString QmitkIOUtil::Impl::s_InvalidFilenameCharacters = "<>:\"/\\|?*";
#else
QString QmitkIOUtil::Impl::s_InvalidFilenameCharacters = "/";
#endif
struct MimeTypeComparison
{
MimeTypeComparison(const std::string &mimeTypeName) : m_Name(mimeTypeName) {}
bool operator()(const mitk::MimeType &mimeType) const { return mimeType.GetName() == m_Name; }
const std::string m_Name;
};
QString QmitkIOUtil::GetFileOpenFilterString()
{
QString filters;
mitk::CoreServicePointer<mitk::IMimeTypeProvider> mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider());
std::vector<std::string> categories = mimeTypeProvider->GetCategories();
for (std::vector<std::string>::iterator cat = categories.begin(); cat != categories.end(); ++cat)
{
QSet<QString> filterExtensions;
std::vector<mitk::MimeType> mimeTypes = mimeTypeProvider->GetMimeTypesForCategory(*cat);
for (std::vector<mitk::MimeType>::iterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
{
std::vector<std::string> extensions = mt->GetExtensions();
for (std::vector<std::string>::iterator ext = extensions.begin(); ext != extensions.end(); ++ext)
{
filterExtensions << QString::fromStdString(*ext);
}
}
QString filter = QString::fromStdString(*cat) + " (";
foreach (const QString &extension, filterExtensions)
{
filter += "*." + extension + " ";
}
filter = filter.replace(filter.size() - 1, 1, ')');
filters += ";;" + filter;
}
filters.prepend("All (*)");
return filters;
}
QList<mitk::BaseData::Pointer> QmitkIOUtil::Load(const QStringList &paths, QWidget *parent)
{
std::vector<LoadInfo> loadInfos;
foreach (const QString &file, paths)
{
loadInfos.push_back(LoadInfo(file.toLocal8Bit().constData()));
}
Impl::ReaderOptionsDialogFunctor optionsCallback;
std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback);
if (!errMsg.empty())
{
QMessageBox::warning(parent, "Error reading files", QString::fromStdString(errMsg));
mitkThrow() << errMsg;
}
QList<mitk::BaseData::Pointer> qResult;
for (std::vector<LoadInfo>::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd;
++iter)
{
for (const auto &elem : iter->m_Output)
{
qResult << elem;
}
}
return qResult;
}
mitk::DataStorage::SetOfObjects::Pointer QmitkIOUtil::Load(const QStringList &paths,
mitk::DataStorage &storage,
QWidget *parent)
{
std::vector<LoadInfo> loadInfos;
foreach (const QString &file, paths)
{
loadInfos.push_back(LoadInfo(file.toLocal8Bit().constData()));
}
mitk::DataStorage::SetOfObjects::Pointer nodeResult = mitk::DataStorage::SetOfObjects::New();
Impl::ReaderOptionsDialogFunctor optionsCallback;
std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback);
if (!errMsg.empty())
{
QMessageBox::warning(parent, "Error reading files", QString::fromStdString(errMsg));
}
return nodeResult;
}
QList<mitk::BaseData::Pointer> QmitkIOUtil::Load(const QString &path, QWidget *parent)
{
QStringList paths;
paths << path;
return Load(paths, parent);
}
mitk::DataStorage::SetOfObjects::Pointer QmitkIOUtil::Load(const QString &path,
mitk::DataStorage &storage,
QWidget *parent)
{
QStringList paths;
paths << path;
return Load(paths, storage, parent);
}
QString QmitkIOUtil::Save(const mitk::BaseData *data,
const QString &defaultBaseName,
const QString &defaultPath,
QWidget *parent,
bool setPathProperty)
{
std::vector<const mitk::BaseData *> dataVector;
dataVector.push_back(data);
QStringList defaultBaseNames;
defaultBaseNames.push_back(defaultBaseName);
return Save(dataVector, defaultBaseNames, defaultPath, parent, setPathProperty).back();
}
QStringList QmitkIOUtil::Save(const std::vector<const mitk::BaseData *> &data,
const QStringList &defaultBaseNames,
const QString &defaultPath,
QWidget *parent,
bool setPathProperty)
{
QStringList fileNames;
QString currentPath = defaultPath;
std::vector<SaveInfo> saveInfos;
int counter = 0;
for (std::vector<const mitk::BaseData *>::const_iterator dataIter = data.begin(), dataIterEnd = data.end();
dataIter != dataIterEnd;
++dataIter, ++counter)
{
SaveInfo saveInfo(*dataIter, mitk::MimeType(), std::string());
SaveFilter filters(saveInfo);
// If there is only the "__all__" filter string, it means there is no writer for this base data
if (filters.Size() < 2)
{
QMessageBox::warning(
parent,
"Saving not possible",
QString("No writer available for type \"%1\"").arg(QString::fromStdString((*dataIter)->GetNameOfClass())));
continue;
}
// Construct a default path and file name
QString filterString = filters.ToString();
QString selectedFilter = filters.GetDefaultFilter();
QString fileName = currentPath;
QString dialogTitle = "Save " + QString::fromStdString((*dataIter)->GetNameOfClass());
if (counter < defaultBaseNames.size())
{
dialogTitle += " \"" + defaultBaseNames[counter] + "\"";
fileName += QDir::separator() + defaultBaseNames[counter];
// We do not append an extension to the file name by default. The extension
// is chosen by the user by either selecting a filter or writing the
// extension in the file name himself (in the file save dialog).
/*
QString defaultExt = filters.GetDefaultExtension();
if (!defaultExt.isEmpty())
{
fileName += "." + defaultExt;
}
*/
}
// Ask the user for a file name
QString nextName = QFileDialog::getSaveFileName(parent, dialogTitle, fileName, filterString, &selectedFilter);
if (Impl::IsIllegalFilename(nextName))
{
QMessageBox::warning(
parent,
"Saving not possible",
QString("File \"%2\" contains invalid characters.\n\nPlease avoid any of \"%1\"")
.arg(Impl::s_InvalidFilenameCharacters.split("", Qt::SkipEmptyParts).join(" "))
.arg(nextName));
continue;
}
if (nextName.isEmpty())
{
// We stop asking for further file names, but we still save the
// data where the user already confirmed the save dialog.
break;
}
fileName = nextName;
std::string stdFileName = fileName.toLocal8Bit().constData();
QFileInfo fileInfo(fileName);
currentPath = fileInfo.absolutePath();
QString suffix = fileInfo.completeSuffix();
mitk::MimeType filterMimeType = filters.GetMimeTypeForFilter(selectedFilter);
mitk::MimeType selectedMimeType;
if (fileInfo.exists() && !fileInfo.isFile())
{
QMessageBox::warning(parent, "Saving not possible", QString("The path \"%1\" is not a file").arg(fileName));
continue;
}
// Theoretically, the user could have entered an extension that does not match the selected filter
// The extension then has prioritry over the filter
// Check if one of the available mime-types match the filename
std::vector<mitk::MimeType> filterMimeTypes = filters.GetMimeTypes();
for (std::vector<mitk::MimeType>::const_iterator mimeTypeIter = filterMimeTypes.begin(),
mimeTypeIterEnd = filterMimeTypes.end();
mimeTypeIter != mimeTypeIterEnd;
++mimeTypeIter)
{
if (mimeTypeIter->MatchesExtension(stdFileName))
{
selectedMimeType = *mimeTypeIter;
break;
}
}
if (!selectedMimeType.IsValid())
{
// The file name either does not contain an extension or the
// extension is unknown.
// If the file already exists, we stop here because we are unable
// to (over)write the file without adding a custom suffix. If the file
// does not exist, we add the default extension from the currently
// selected filter. If the "All" filter was selected, we only add the
// default extensions if the file name itself does not already contain
// an extension.
if (!fileInfo.exists())
{
if (filterMimeType == SaveFilter::ALL_MIMETYPE())
{
if (suffix.isEmpty())
{
// Use the highest ranked mime-type from the list
selectedMimeType = filters.GetDefaultMimeType();
}
}
else
{
selectedMimeType = filterMimeType;
}
if (selectedMimeType.IsValid())
{
suffix = QString::fromStdString(selectedMimeType.GetExtensions().front());
fileName += "." + suffix;
stdFileName = fileName.toLocal8Bit().constData();
// We changed the file name (added a suffix) so ask in case
- // the file aready exists.
+ // the file already exists.
fileInfo = QFileInfo(fileName);
if (fileInfo.exists())
{
if (!fileInfo.isFile())
{
QMessageBox::warning(
parent, "Saving not possible", QString("The path \"%1\" is not a file").arg(fileName));
continue;
}
if (QMessageBox::question(
parent,
"Replace File",
QString("A file named \"%1\" already exists. Do you want to replace it?").arg(fileName)) ==
QMessageBox::No)
{
continue;
}
}
}
}
}
if (!selectedMimeType.IsValid())
{
// The extension/filename is not valid (no mime-type found), bail out
QMessageBox::warning(
parent, "Saving not possible", QString("No mime-type available which can handle \"%1\".").arg(fileName));
continue;
}
if (!QFileInfo(fileInfo.absolutePath()).isWritable())
{
QMessageBox::warning(parent, "Saving not possible", QString("The path \"%1\" is not writable").arg(fileName));
continue;
}
fileNames.push_back(fileName);
saveInfo.m_Path = stdFileName;
saveInfo.m_MimeType = selectedMimeType;
// pre-select the best writer for the chosen mime-type
saveInfo.m_WriterSelector.Select(selectedMimeType.GetName());
saveInfos.push_back(saveInfo);
}
if (!saveInfos.empty())
{
Impl::WriterOptionsDialogFunctor optionsCallback;
std::string errMsg = Save(saveInfos, &optionsCallback, setPathProperty);
if (!errMsg.empty())
{
QMessageBox::warning(parent, "Error writing files", QString::fromStdString(errMsg));
mitkThrow() << errMsg;
}
}
return fileNames;
}
void QmitkIOUtil::SaveBaseDataWithDialog(mitk::BaseData *data, std::string fileName, QWidget * /*parent*/)
{
Save(data, fileName);
}
void QmitkIOUtil::SaveSurfaceWithDialog(mitk::Surface::Pointer surface, std::string fileName, QWidget * /*parent*/)
{
Save(surface, fileName);
}
void QmitkIOUtil::SaveImageWithDialog(mitk::Image::Pointer image, std::string fileName, QWidget * /*parent*/)
{
Save(image, fileName);
}
void QmitkIOUtil::SavePointSetWithDialog(mitk::PointSet::Pointer pointset, std::string fileName, QWidget * /*parent*/)
{
Save(pointset, fileName);
}
struct QmitkIOUtil::SaveFilter::Impl
{
Impl(const mitk::IOUtil::SaveInfo &saveInfo) : m_SaveInfo(saveInfo)
{
- // Add an artifical filter for "All"
+ // Add an artificial filter for "All"
m_MimeTypes.push_back(ALL_MIMETYPE());
m_FilterStrings.push_back("All (*.*)");
// Get all writers and their mime types for the given base data type
// (this is sorted already)
std::vector<mitk::MimeType> mimeTypes = saveInfo.m_WriterSelector.GetMimeTypes();
for (std::vector<mitk::MimeType>::const_reverse_iterator iter = mimeTypes.rbegin(), iterEnd = mimeTypes.rend();
iter != iterEnd;
++iter)
{
QList<QString> filterExtensions;
mitk::MimeType mimeType = *iter;
std::vector<std::string> extensions = mimeType.GetExtensions();
for (auto &extension : extensions)
{
filterExtensions << QString::fromStdString(extension);
}
if (m_DefaultExtension.isEmpty())
{
m_DefaultExtension = QString::fromStdString(extensions.front());
}
QString filter = QString::fromStdString(mimeType.GetComment()) + " (";
foreach (const QString &extension, filterExtensions)
{
filter += "*." + extension + " ";
}
filter = filter.replace(filter.size() - 1, 1, ')');
m_MimeTypes.push_back(mimeType);
m_FilterStrings.push_back(filter);
}
}
const mitk::IOUtil::SaveInfo m_SaveInfo;
std::vector<mitk::MimeType> m_MimeTypes;
QStringList m_FilterStrings;
QString m_DefaultExtension;
};
mitk::MimeType QmitkIOUtil::SaveFilter::ALL_MIMETYPE()
{
static mitk::CustomMimeType allMimeType(std::string("__all__"));
return mitk::MimeType(allMimeType, -1, -1);
}
QmitkIOUtil::SaveFilter::SaveFilter(const QmitkIOUtil::SaveFilter &other) : d(new Impl(*other.d))
{
}
QmitkIOUtil::SaveFilter::SaveFilter(const SaveInfo &saveInfo) : d(new Impl(saveInfo))
{
}
QmitkIOUtil::SaveFilter &QmitkIOUtil::SaveFilter::operator=(const QmitkIOUtil::SaveFilter &other)
{
d.reset(new Impl(*other.d));
return *this;
}
std::vector<mitk::MimeType> QmitkIOUtil::SaveFilter::GetMimeTypes() const
{
return d->m_MimeTypes;
}
QString QmitkIOUtil::SaveFilter::GetFilterForMimeType(const std::string &mimeType) const
{
std::vector<mitk::MimeType>::const_iterator iter =
std::find_if(d->m_MimeTypes.begin(), d->m_MimeTypes.end(), MimeTypeComparison(mimeType));
if (iter == d->m_MimeTypes.end())
{
return QString();
}
int index = static_cast<int>(iter - d->m_MimeTypes.begin());
if (index < 0 || index >= d->m_FilterStrings.size())
{
return QString();
}
return d->m_FilterStrings[index];
}
mitk::MimeType QmitkIOUtil::SaveFilter::GetMimeTypeForFilter(const QString &filter) const
{
int index = d->m_FilterStrings.indexOf(filter);
if (index < 0)
{
return mitk::MimeType();
}
return d->m_MimeTypes[index];
}
QString QmitkIOUtil::SaveFilter::GetDefaultFilter() const
{
if (d->m_FilterStrings.size() > 1)
{
return d->m_FilterStrings.at(1);
}
else if (d->m_FilterStrings.size() > 0)
{
return d->m_FilterStrings.front();
}
return QString();
}
QString QmitkIOUtil::SaveFilter::GetDefaultExtension() const
{
return d->m_DefaultExtension;
}
mitk::MimeType QmitkIOUtil::SaveFilter::GetDefaultMimeType() const
{
if (d->m_MimeTypes.size() > 1)
{
return d->m_MimeTypes.at(1);
}
else if (d->m_MimeTypes.size() > 0)
{
return d->m_MimeTypes.front();
}
return mitk::MimeType();
}
QString QmitkIOUtil::SaveFilter::ToString() const
{
return d->m_FilterStrings.join(";;");
}
int QmitkIOUtil::SaveFilter::Size() const
{
return d->m_FilterStrings.size();
}
bool QmitkIOUtil::SaveFilter::IsEmpty() const
{
return d->m_FilterStrings.isEmpty();
}
bool QmitkIOUtil::SaveFilter::ContainsMimeType(const std::string &mimeType)
{
return std::find_if(d->m_MimeTypes.begin(), d->m_MimeTypes.end(), MimeTypeComparison(mimeType)) !=
d->m_MimeTypes.end();
}
diff --git a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp
index 65fe3ad56b..bda5298a5a 100644
--- a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp
+++ b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp
@@ -1,530 +1,530 @@
/*============================================================================
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 "QmitkPropertiesTableModel.h"
//# Own includes
#include "QmitkCustomVariants.h"
#include "mitkColorProperty.h"
#include "mitkEnumerationProperty.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include "mitkStringProperty.h"
//# Toolkit includes
#include <QBrush>
#include <QColor>
#include <QStringList>
#include <itkCommand.h>
//# PUBLIC CTORS,DTOR
QmitkPropertiesTableModel::QmitkPropertiesTableModel(QObject *parent, mitk::PropertyList::Pointer _PropertyList)
: QAbstractTableModel(parent),
m_PropertyList(nullptr),
m_BlockEvents(false),
m_SortDescending(false),
m_FilterKeyWord("")
{
this->SetPropertyList(_PropertyList);
}
QmitkPropertiesTableModel::~QmitkPropertiesTableModel()
{
// remove all event listeners by setting the property list to 0
this->SetPropertyList(nullptr);
}
//# PUBLIC GETTER
mitk::PropertyList::Pointer QmitkPropertiesTableModel::GetPropertyList() const
{
return m_PropertyList.Lock();
}
Qt::ItemFlags QmitkPropertiesTableModel::flags(const QModelIndex &index) const
{
// no editing so far, return default (enabled, selectable)
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.column() == PROPERTY_VALUE_COLUMN)
{
// there are also read only property items -> do not allow editing them
if (index.data(Qt::EditRole).isValid())
flags |= Qt::ItemIsEditable;
if (index.data(Qt::CheckStateRole).isValid())
flags |= Qt::ItemIsUserCheckable;
}
return flags;
}
QVariant QmitkPropertiesTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
{
switch (section)
{
case PROPERTY_NAME_COLUMN:
return tr("Name");
case PROPERTY_VALUE_COLUMN:
return tr("Value");
default:
return QVariant();
}
}
return QVariant();
}
QVariant QmitkPropertiesTableModel::data(const QModelIndex &index, int role) const
{
// empty data by default
QVariant data;
if (!index.isValid() || m_SelectedProperties.empty() || index.row() > (int)(m_SelectedProperties.size() - 1))
return data;
// the properties name
if (index.column() == PROPERTY_NAME_COLUMN)
{
if (role == Qt::DisplayRole)
data = QString::fromStdString(m_SelectedProperties[index.row()].first);
}
// the real properties value
else if (index.column() == PROPERTY_VALUE_COLUMN)
{
mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second;
if (const mitk::ColorProperty *colorProp = dynamic_cast<const mitk::ColorProperty *>(baseProp))
{
mitk::Color col = colorProp->GetColor();
QColor qcol((int)(col.GetRed() * 255), (int)(col.GetGreen() * 255), (int)(col.GetBlue() * 255));
if (role == Qt::DisplayRole)
data.setValue(qcol);
else if (role == Qt::EditRole)
data.setValue(qcol);
}
else if (mitk::BoolProperty *boolProp = dynamic_cast<mitk::BoolProperty *>(baseProp))
{
if (role == Qt::CheckStateRole)
data = boolProp->GetValue() ? Qt::Checked : Qt::Unchecked;
}
else if (mitk::StringProperty *stringProp = dynamic_cast<mitk::StringProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<QString>(QString::fromStdString(stringProp->GetValue()));
else if (role == Qt::EditRole)
data.setValue<QString>(QString::fromStdString(stringProp->GetValue()));
}
else if (mitk::IntProperty *intProp = dynamic_cast<mitk::IntProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<int>(intProp->GetValue());
else if (role == Qt::EditRole)
data.setValue<int>(intProp->GetValue());
}
else if (mitk::FloatProperty *floatProp = dynamic_cast<mitk::FloatProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<float>(floatProp->GetValue());
else if (role == Qt::EditRole)
data.setValue<float>(floatProp->GetValue());
}
else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast<mitk::EnumerationProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<QString>(QString::fromStdString(baseProp->GetValueAsString()));
else if (role == Qt::EditRole)
{
QStringList values;
for (auto it = enumerationProp->Begin(); it != enumerationProp->End(); it++)
{
values << QString::fromStdString(it->second);
}
data.setValue(values);
}
}
else
{
if (role == Qt::DisplayRole)
data.setValue(QString::fromStdString(m_SelectedProperties[index.row()].second->GetValueAsString()));
}
}
return data;
}
int QmitkPropertiesTableModel::rowCount(const QModelIndex & /*parent*/) const
{
// return the number of properties in the properties list.
return m_SelectedProperties.size();
}
int QmitkPropertiesTableModel::columnCount(const QModelIndex & /*parent*/) const
{
return 2;
}
//# PUBLIC SETTER
void QmitkPropertiesTableModel::SetPropertyList(mitk::PropertyList *_PropertyList)
{
// if propertylist really changed
if (m_PropertyList != _PropertyList)
{
auto propertyList = m_PropertyList.Lock();
// Remove delete listener if there was a propertylist before
if (propertyList.IsNotNull())
propertyList->RemoveObserver(m_PropertyListDeleteObserverTag);
// set new list
m_PropertyList = _PropertyList;
propertyList = m_PropertyList.Lock();
if (propertyList.IsNotNull())
{
auto command = itk::SimpleMemberCommand<QmitkPropertiesTableModel>::New();
command->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyListDelete);
m_PropertyListDeleteObserverTag = propertyList->AddObserver(itk::DeleteEvent(), command);
}
this->Reset();
}
}
void QmitkPropertiesTableModel::PropertyListDelete()
{
if (!m_BlockEvents)
{
m_BlockEvents = true;
this->Reset();
m_BlockEvents = false;
}
}
void QmitkPropertiesTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject & /*event*/)
{
if (!m_BlockEvents)
{
m_BlockEvents = true;
int row = this->FindProperty(dynamic_cast<const mitk::BaseProperty *>(caller));
QModelIndex indexOfChangedProperty = index(row, 1);
emit dataChanged(indexOfChangedProperty, indexOfChangedProperty);
m_BlockEvents = false;
}
}
void QmitkPropertiesTableModel::PropertyDelete(const itk::Object *caller, const itk::EventObject & /*event*/)
{
if (!m_BlockEvents)
{
m_BlockEvents = true;
int row = this->FindProperty(dynamic_cast<const mitk::BaseProperty *>(caller));
if (row >= 0)
this->Reset();
m_BlockEvents = false;
}
}
bool QmitkPropertiesTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// TODO: check 'role' condition
if (index.isValid() && !m_SelectedProperties.empty() && index.row() < (int)(m_SelectedProperties.size()) &&
(role == Qt::EditRole || role == Qt::CheckStateRole))
{
// block all events now!
m_BlockEvents = true;
auto propertyList = m_PropertyList.Lock();
// the properties name
if (index.column() == PROPERTY_VALUE_COLUMN)
{
mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second;
if (mitk::ColorProperty *colorProp = dynamic_cast<mitk::ColorProperty *>(baseProp))
{
QColor qcolor = value.value<QColor>();
if (!qcolor.isValid())
return false;
mitk::Color col = colorProp->GetColor();
col.SetRed(qcolor.red() / 255.0);
col.SetGreen(qcolor.green() / 255.0);
col.SetBlue(qcolor.blue() / 255.0);
colorProp->SetColor(col);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (mitk::BoolProperty *boolProp = dynamic_cast<mitk::BoolProperty *>(baseProp))
{
boolProp->SetValue(value.toInt() == Qt::Checked ? true : false);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (mitk::StringProperty *stringProp = dynamic_cast<mitk::StringProperty *>(baseProp))
{
stringProp->SetValue((value.value<QString>()).toStdString());
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (mitk::IntProperty *intProp = dynamic_cast<mitk::IntProperty *>(baseProp))
{
int intValue = value.value<int>();
if (intValue != intProp->GetValue())
{
intProp->SetValue(intValue);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
else if (mitk::FloatProperty *floatProp = dynamic_cast<mitk::FloatProperty *>(baseProp))
{
float floatValue = value.value<float>();
if (floatValue != floatProp->GetValue())
{
floatProp->SetValue(floatValue);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast<mitk::EnumerationProperty *>(baseProp))
{
std::string activatedItem = value.value<QString>().toStdString();
if (activatedItem != enumerationProp->GetValueAsString())
{
if (enumerationProp->IsValidEnumerationValue(activatedItem))
{
enumerationProp->SetValue(activatedItem);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
}
}
// property was changed by us, now we can accept property changes triggered by someone else
m_BlockEvents = false;
emit dataChanged(index, index);
return true;
}
return false;
}
void QmitkPropertiesTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */)
{
bool sortDescending = (order == Qt::DescendingOrder) ? true : false;
// do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!)
if (sortDescending != m_SortDescending)
{
m_SortDescending = sortDescending;
PropertyDataSetCompareFunction::CompareCriteria _CompareCriteria = PropertyDataSetCompareFunction::CompareByName;
PropertyDataSetCompareFunction::CompareOperator _CompareOperator =
m_SortDescending ? PropertyDataSetCompareFunction::Greater : PropertyDataSetCompareFunction::Less;
if (column == PROPERTY_VALUE_COLUMN)
_CompareCriteria = PropertyDataSetCompareFunction::CompareByValue;
PropertyDataSetCompareFunction compareFunc(_CompareCriteria, _CompareOperator);
std::sort(m_SelectedProperties.begin(), m_SelectedProperties.end(), compareFunc);
QAbstractTableModel::beginResetModel();
QAbstractTableModel::endResetModel();
}
}
//# PROTECTED GETTER
int QmitkPropertiesTableModel::FindProperty(const mitk::BaseProperty *_Property) const
{
int row = -1;
if (_Property)
{
// search for property that changed and emit datachanged on the corresponding ModelIndex
std::vector<PropertyDataSet>::const_iterator propertyIterator;
for (propertyIterator = m_SelectedProperties.begin(); propertyIterator != m_SelectedProperties.end();
propertyIterator++)
{
if (propertyIterator->second == _Property)
break;
}
if (propertyIterator != m_SelectedProperties.end())
row = std::distance(m_SelectedProperties.begin(), propertyIterator);
}
return row;
}
//# PROTECTED SETTER
void QmitkPropertiesTableModel::AddSelectedProperty(PropertyDataSet &_PropertyDataSet)
{
// subscribe for modified event
itk::MemberCommand<QmitkPropertiesTableModel>::Pointer _PropertyDataSetModifiedCommand =
itk::MemberCommand<QmitkPropertiesTableModel>::New();
_PropertyDataSetModifiedCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyModified);
m_PropertyModifiedObserverTags.push_back(
_PropertyDataSet.second->AddObserver(itk::ModifiedEvent(), _PropertyDataSetModifiedCommand));
// subscribe for delete event
itk::MemberCommand<QmitkPropertiesTableModel>::Pointer _PropertyDataSetDeleteCommand =
itk::MemberCommand<QmitkPropertiesTableModel>::New();
_PropertyDataSetDeleteCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyDelete);
m_PropertyDeleteObserverTags.push_back(
_PropertyDataSet.second->AddObserver(itk::DeleteEvent(), _PropertyDataSetDeleteCommand));
// add to the selection
m_SelectedProperties.push_back(_PropertyDataSet);
}
void QmitkPropertiesTableModel::RemoveSelectedProperty(unsigned int _Index)
{
PropertyDataSet &_PropertyDataSet = m_SelectedProperties.at(_Index);
// remove modified event listener
_PropertyDataSet.second->RemoveObserver(m_PropertyModifiedObserverTags[_Index]);
m_PropertyModifiedObserverTags.erase(m_PropertyModifiedObserverTags.begin() + _Index);
// remove delete event listener
_PropertyDataSet.second->RemoveObserver(m_PropertyDeleteObserverTags[_Index]);
m_PropertyDeleteObserverTags.erase(m_PropertyDeleteObserverTags.begin() + _Index);
// remove from selection
m_SelectedProperties.erase(m_SelectedProperties.begin() + _Index);
}
void QmitkPropertiesTableModel::Reset()
{
// remove all selected properties
while (!m_SelectedProperties.empty())
{
this->RemoveSelectedProperty(m_SelectedProperties.size() - 1);
}
std::vector<PropertyDataSet> allPredicates;
auto propertyList = m_PropertyList.Lock();
if (propertyList.IsNotNull())
{
// first of all: collect all properties from the list
for (auto it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); it++)
{
allPredicates.push_back(*it); //% TODO
}
}
// make a subselection if a keyword is specified
if (!m_FilterKeyWord.empty())
{
std::vector<PropertyDataSet> subSelection;
for (auto it = allPredicates.begin(); it != allPredicates.end(); it++)
{
// add this to the selection if it is matched by the keyword
if ((*it).first.find(m_FilterKeyWord) != std::string::npos)
subSelection.push_back((*it));
}
allPredicates.clear();
allPredicates = subSelection;
}
PropertyDataSet tmpPropertyDataSet;
// add all selected now to the Model
for (auto it = allPredicates.begin(); it != allPredicates.end(); it++)
{
tmpPropertyDataSet = *it;
this->AddSelectedProperty(tmpPropertyDataSet);
}
// sort the list as indicated by m_SortDescending
this->sort(m_SortDescending);
- // model was resetted
+ // model was reset
QAbstractTableModel::beginResetModel();
QAbstractTableModel::endResetModel();
}
void QmitkPropertiesTableModel::SetFilterPropertiesKeyWord(std::string _FilterKeyWord)
{
m_FilterKeyWord = _FilterKeyWord;
this->Reset();
}
QmitkPropertiesTableModel::PropertyDataSetCompareFunction::PropertyDataSetCompareFunction(
CompareCriteria _CompareCriteria, CompareOperator _CompareOperator)
: m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator)
{
}
bool QmitkPropertiesTableModel::PropertyDataSetCompareFunction::operator()(const PropertyDataSet &_Left,
const PropertyDataSet &_Right) const
{
switch (m_CompareCriteria)
{
case CompareByValue:
if (m_CompareOperator == Less)
return (_Left.second->GetValueAsString() < _Right.second->GetValueAsString());
else
return (_Left.second->GetValueAsString() > _Right.second->GetValueAsString());
break;
// CompareByName:
default:
if (m_CompareOperator == Less)
return (_Left.first < _Right.first);
else
return (_Left.first > _Right.first);
break;
}
}
QmitkPropertiesTableModel::PropertyListElementFilterFunction::PropertyListElementFilterFunction(
const std::string &_FilterKeyWord)
: m_FilterKeyWord(_FilterKeyWord)
{
}
bool QmitkPropertiesTableModel::PropertyListElementFilterFunction::operator()(const PropertyDataSet &_Elem) const
{
if (m_FilterKeyWord.empty())
return true;
return (_Elem.first.find(m_FilterKeyWord) == 0);
}
diff --git a/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp
index f1d8bbbc7a..85761b394e 100644
--- a/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp
+++ b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp
@@ -1,708 +1,708 @@
/*============================================================================
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 qt widgets module
#include <QmitkSynchronizedNodeSelectionWidget.h>
#include <QmitkCustomVariants.h>
#include <QmitkEnums.h>
#include <QmitkNodeSelectionDialog.h>
// mitk core module
#include <mitkImage.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateProperty.h>
#include <mitkRenderWindowLayerUtilities.h>
QmitkSynchronizedNodeSelectionWidget::QmitkSynchronizedNodeSelectionWidget(QWidget* parent)
: QmitkAbstractNodeSelectionWidget(parent)
{
m_Controls.setupUi(this);
m_StorageModel = std::make_unique<QmitkRenderWindowDataNodeTableModel>(this);
m_Controls.tableView->setModel(m_StorageModel.get());
m_Controls.tableView->horizontalHeader()->setVisible(false);
m_Controls.tableView->verticalHeader()->setVisible(false);
m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection);
m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu);
m_Controls.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_Controls.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
this->SetUpConnections();
this->Initialize();
}
QmitkSynchronizedNodeSelectionWidget::~QmitkSynchronizedNodeSelectionWidget()
{
bool isSynchronized = this->IsSynchronized();
if (isSynchronized)
{
emit DeregisterSynchronization();
return;
}
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
// If the model is not synchronized,
// we know that renderer-specific properties exist for all nodes.
// These properties need to be removed from the nodes.
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// Delete the relevant renderer-specific properties for the node using the current base renderer.
mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, baseRenderer);
}
}
void QmitkSynchronizedNodeSelectionWidget::SetBaseRenderer(mitk::BaseRenderer* baseRenderer)
{
if (m_BaseRenderer == baseRenderer)
{
// no need to do something
return;
}
if (nullptr == baseRenderer)
{
return;
}
auto oldBaseRenderer = m_BaseRenderer.Lock();
m_BaseRenderer = baseRenderer;
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
bool isSynchronized = this->IsSynchronized();
if (isSynchronized)
{
// If the model is synchronized,
// all nodes use global / default properties.
// No renderer-specific property lists should exist
// so there is no need to transfer any property values.
}
else
{
// If the model is not synchronized,
// we know that renderer-specific properties exist for all nodes.
// These properties need to be removed from the nodes and
// we need to transfer their values to new renderer-specific properties.
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// Set the relevant renderer-specific properties for the node using the new base renderer.
// By transferring the values from the old property list,
// the same property-state is kept when switching to another base renderer.
mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(node, baseRenderer, oldBaseRenderer);
// Delete the relevant renderer-specific properties for the node using the old base renderer.
mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, oldBaseRenderer);
}
}
this->Initialize();
}
void QmitkSynchronizedNodeSelectionWidget::SetSelectAll(bool selectAll)
{
if (selectAll == m_Controls.selectionModeCheckBox->isChecked())
{
// no need to do something
return;
}
m_Controls.selectionModeCheckBox->setChecked(selectAll);
}
bool QmitkSynchronizedNodeSelectionWidget::GetSelectAll() const
{
return m_Controls.selectionModeCheckBox->isChecked();
}
void QmitkSynchronizedNodeSelectionWidget::SetSynchronized(bool synchronize)
{
if (synchronize == this->IsSynchronized())
{
// no need to do something
return;
}
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
if (synchronize)
{
// set the base renderer of the model to nullptr, such that global properties are used
m_StorageModel->SetCurrentRenderer(nullptr);
// If the model is synchronized,
// we know that the model was not synchronized before.
// That means that all nodes use renderer-specific properties,
// but now all nodes need global properties.
// Thus we need to remove the renderer-specific properties of all nodes of the
// datastorage.
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// For helper / hidden nodes:
// If the node predicate does not match, do not remove the renderer-specific property
// This is relevant for the crosshair data nodes, which are only visible inside their
// corresponding render window.
if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))
{
// Delete the relevant renderer-specific properties for the node using the current base renderer.
mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, baseRenderer);
}
}
}
else
{
// set the base renderer of the model to current base renderer, such that renderer-specific properties are used
m_StorageModel->SetCurrentRenderer(baseRenderer);
// If the model is not synchronized anymore,
// we know that the model was synchronized before.
// That means that all nodes use global / default properties,
// but now all nodes need renderer-specific properties.
// Thus we need to modify the renderer-specific properties of all nodes of the
// datastorage:
// - hide those nodes, which are not part of the newly selected nodes.
// - keep the property values of those nodes, which are part of the new selection AND
// have been selected before
auto currentNodeSelection = this->GetCurrentInternalSelection();
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// check if the node is part of the current selection
auto finding = std::find(std::begin(currentNodeSelection), std::end(currentNodeSelection), node);
if (finding != std::end(currentNodeSelection)) // node found / part of the current selection
{
- // Set the relevant renderer-specific properties for the node using the curent base renderer.
+ // Set the relevant renderer-specific properties for the node using the current base renderer.
// By transferring the values from the global / default property list,
// the same property-state is kept when switching to non-synchronized mode.
mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(node, baseRenderer, nullptr);
}
else
{
// If the node is not part of the selection, unset the relevant renderer-specific properties.
// This will unset the "visible" and "layer" property for the renderer-specific property list and
// hide the node for this renderer.
// ATTENTION: This is required, since the synchronized property needs to be overwritten
// to make sure that the visibility is correctly set for the specific base renderer.
this->DeselectNode(node);
}
}
}
// Since the synchronization might lead to a different node order depending on the layer properties, the render window
// needs to be updated.
// Explicitly request an update since a renderer-specific property change does not mark the node as modified.
// see https://phabricator.mitk.org/T22322
mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow());
}
bool QmitkSynchronizedNodeSelectionWidget::IsSynchronized() const
{
return m_StorageModel->GetCurrentRenderer().IsNull();
}
void QmitkSynchronizedNodeSelectionWidget::OnSelectionModeChanged(bool selectAll)
{
emit SelectionModeChanged(selectAll);
if (selectAll)
{
this->SelectAll();
}
}
void QmitkSynchronizedNodeSelectionWidget::OnEditSelection()
{
QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this);
dialog->SetDataStorage(m_DataStorage.Lock());
dialog->SetNodePredicate(m_NodePredicate);
dialog->SetCurrentSelection(m_StorageModel->GetCurrentSelection());
dialog->SetSelectionMode(QAbstractItemView::MultiSelection);
m_Controls.changeSelectionButton->setChecked(true);
if (dialog->exec())
{
m_Controls.selectionModeCheckBox->setChecked(false);
emit SelectionModeChanged(false);
auto selectedNodes = dialog->GetSelectedNodes();
this->HandleChangeOfInternalSelection(selectedNodes);
}
m_Controls.changeSelectionButton->setChecked(false);
delete dialog;
}
void QmitkSynchronizedNodeSelectionWidget::OnTableClicked(const QModelIndex& index)
{
if (!index.isValid() || m_StorageModel.get() != index.model())
{
return;
}
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
QVariant dataNodeVariant = index.data(QmitkDataNodeRole);
auto dataNode = dataNodeVariant.value<mitk::DataNode::Pointer>();
if (index.column() == 1) // node visibility column
{
bool visibiliy = index.data(Qt::EditRole).toBool();
m_StorageModel->setData(index, QVariant(!visibiliy), Qt::EditRole);
return;
}
if (index.column() == 2) // reinit node column
{
this->ReinitNode(dataNode);
return;
}
if (index.column() == 3) // remove node column
{
this->RemoveFromInternalSelection(dataNode);
return;
}
}
void QmitkSynchronizedNodeSelectionWidget::SetUpConnections()
{
connect(m_Controls.selectionModeCheckBox, &QCheckBox::clicked,
this, &QmitkSynchronizedNodeSelectionWidget::OnSelectionModeChanged);
connect(m_Controls.changeSelectionButton, &QPushButton::clicked,
this, &QmitkSynchronizedNodeSelectionWidget::OnEditSelection);
connect(m_Controls.tableView, &QTableView::clicked,
this, &QmitkSynchronizedNodeSelectionWidget::OnTableClicked);
}
void QmitkSynchronizedNodeSelectionWidget::SetSelection(const NodeList& newSelection)
{
this->HandleChangeOfInternalSelection(newSelection);
m_Controls.selectionModeCheckBox->setChecked(false);
}
void QmitkSynchronizedNodeSelectionWidget::Initialize()
{
auto baseRenderer = m_BaseRenderer.Lock();
auto dataStorage = m_DataStorage.Lock();
m_StorageModel->SetDataStorage(dataStorage);
m_StorageModel->SetCurrentRenderer(baseRenderer);
if (baseRenderer.IsNull() || dataStorage.IsNull())
{
m_Controls.selectionModeCheckBox->setEnabled(false);
m_Controls.changeSelectionButton->setEnabled(false);
// reset the model if no data storage is defined
m_StorageModel->removeRows(0, m_StorageModel->rowCount());
return;
}
// Use the new data storage / node predicate to correctly set the list of
// currently selected data nodes for the model.
// If a new data storage or node predicate has been defined,
// we switch to the "selectAll" mode and synchronize the selection for simplicity.
// enable UI
m_Controls.selectionModeCheckBox->setEnabled(true);
m_Controls.changeSelectionButton->setEnabled(true);
m_Controls.selectionModeCheckBox->setChecked(true);
// set the base renderer of the model to nullptr, such that global properties are used (synchronized mode)
m_StorageModel->SetCurrentRenderer(nullptr);
}
void QmitkSynchronizedNodeSelectionWidget::UpdateInfo()
{
}
void QmitkSynchronizedNodeSelectionWidget::OnDataStorageChanged()
{
this->Initialize();
}
void QmitkSynchronizedNodeSelectionWidget::OnNodePredicateChanged()
{
this->Initialize();
}
void QmitkSynchronizedNodeSelectionWidget::ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
bool isSynchronized = this->IsSynchronized();
if (isSynchronized)
{
this->ReviseSynchronizedSelectionChanged(oldInternalSelection, newInternalSelection);
}
else
{
this->ReviseDesynchronizedSelectionChanged(oldInternalSelection, newInternalSelection);
}
// Since a new selection might have a different rendering tree the render windows
// need to be updated.
// Explicitly request an update since a renderer-specific property change does not mark the node as modified.
// see https://phabricator.mitk.org/T22322
mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow());
}
void QmitkSynchronizedNodeSelectionWidget::OnInternalSelectionChanged()
{
m_StorageModel->SetCurrentSelection(this->GetCurrentInternalSelection());
}
bool QmitkSynchronizedNodeSelectionWidget::AllowEmissionOfSelection(const NodeList& /*emissionCandidates*/) const
{
return this->IsSynchronized();
}
void QmitkSynchronizedNodeSelectionWidget::OnNodeAddedToStorage(const mitk::DataNode* node)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
// For helper / hidden nodes
if (m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node))
{
// If the node predicate does not match, do not add the node to the current selection.
// Leave the visibility as it is.
return;
}
// The selection mode determines if we want to show all nodes from the data storage
// or use a local selected list of nodes.
// We need to hide each new incoming data node, if we use a local selection,
// since we do not want to show / select newly added nodes immediately.
// We need to add the incoming node to our selection, if the selection mode check box
// is checked.
// We want to add the incoming node to our selection, if the node is a child node
// of an already selected node.
// Nodes added to the selection will be made visible.
if (m_Controls.selectionModeCheckBox->isChecked() || this->IsParentNodeSelected(node))
{
auto currentSelection = this->GetCurrentInternalSelection();
// Check if the nodes is already part of the internal selection.
// That can happen if another render window already added the new node and sent out the new, updated
// selection to be synchronized.
auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), node);
if (finding != std::end(currentSelection)) // node found
{
// node already part of the selection
return;
}
currentSelection.append(const_cast<mitk::DataNode*>(node));
// This function will call 'QmitkSynchronizedNodeSelectionWidget::ReviseSelectionChanged'
// which will take care of the visibility-property for newly added node.
this->HandleChangeOfInternalSelection(currentSelection);
}
else
{
// If the widget is in "local-selection" state (selectionModeCheckBox unchecked),
// the new incoming node needs to be hid.
// Here it depends on the synchronization-state which properties need
// to be modified.
if (this->IsSynchronized())
{
// If the node will not be part of the new selection, hide the node.
const_cast<mitk::DataNode*>(node)->SetVisibility(false);
}
else
{
// If the widget is not synchronized, all nodes use renderer-specific properties.
// Thus we need to modify the renderer-specific properties of the node:
// - hide the node, which is not part of the selection
this->DeselectNode(const_cast<mitk::DataNode*>(node));
}
}
}
void QmitkSynchronizedNodeSelectionWidget::OnNodeModified(const itk::Object* caller, const itk::EventObject& event)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
if (!itk::ModifiedEvent().CheckEvent(&event))
{
return;
}
auto node = dynamic_cast<const mitk::DataNode*>(caller);
if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))
{
auto currentSelection = this->GetCurrentInternalSelection();
// check if the node to be modified is part of the current selection
auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), node);
if (finding == std::end(currentSelection)) // node not found
{
// node not part of the selection
return;
}
// We know that the node is relevant, but we don't know if the node modification was relevant
// for the rendering. We just request an update here.
// Explicitly request an update since a renderer-specific property change does not mark the node as modified.
// see https://phabricator.mitk.org/T22322
mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow());
m_StorageModel->UpdateModelData();
}
}
void QmitkSynchronizedNodeSelectionWidget::ReviseSynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection)
{
// If the model is synchronized, all nodes use global / default properties.
// Thus we need to modify the global properties of the selection:
// - a) show those nodes, which are part of the new selection AND have not been
// selected before
// - b) keep the property values of those nodes, which are part of the new selection AND
// have been selected before
// - c) hide those nodes, which are part of the old selection AND
// have not been newly selected
for (auto& node : newInternalSelection)
{
// check if the node is part of the old selection
auto finding = std::find(std::begin(oldInternalSelection), std::end(oldInternalSelection), node);
if (finding == std::end(oldInternalSelection)) // node not found
{
// If the node is part of the new selection and was not already part of the old selection,
// set the relevant renderer-specific properties.
// This will set the "visible" property for the global / default property list
// and show the node for this renderer.
node->SetVisibility(true); // item a)
}
// else: item b): node that was already selected before does not need to be modified
}
for (auto& node : oldInternalSelection)
{
// check if the node is part of the new selection
auto finding = std::find(std::begin(newInternalSelection), std::end(newInternalSelection), node);
if (finding == std::end(newInternalSelection)) // node not found
{
// If the node is not part of the new selection, hide the node.
node->SetVisibility(false); // item c)
}
// else: item b): node that was already selected before does not need to be modified
}
}
void QmitkSynchronizedNodeSelectionWidget::ReviseDesynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
// If the model is not synchronized, all nodes need renderer-specific properties.
// Thus we need to modify the renderer-specific properties of the selection:
// - a) set the renderer-specific properties of those nodes, which are part of the new selection AND
// have not been selected before (see 'SelectNode')
// - b) show those nodes, which are part of the new selection AND have not been
// selected before
// - c) keep the property values of those nodes, which are part of the new selection AND
// have been selected before
// - d) hide those nodes, which are part of the old selection AND
// have not been newly selected
// - e) set the renderer-specific properties of those nodes, which are part of the old selection AND
// have not been newly selected, to denote which nodes are selected
for (auto& node : newInternalSelection)
{
// check if the node is part of the old selection
auto finding = std::find(std::begin(oldInternalSelection), std::end(oldInternalSelection), node);
if (finding == std::end(oldInternalSelection)) // node not found
{
// If the node is part of the new selection and was not already part of the old selection,
// set the relevant renderer-specific properties.
// This will set the "visible" and "layer" property for the renderer-specific property list
// such that the global / default property list values are overwritten
mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(node, baseRenderer); // item a)
// Explicitly set the visibility to true for selected nodes to show them in the render window.
node->SetVisibility(true, baseRenderer); // item b)
}
// else: item c): node that was already selected before does not need to be modified
}
for (auto& node : oldInternalSelection)
{
// check if the node is part of the new selection
auto finding = std::find(std::begin(newInternalSelection), std::end(newInternalSelection), node);
if (finding == std::end(newInternalSelection)) // node not found
{
// If the node is not part of the new selection, unset the relevant renderer-specific properties.
// This will unset the "visible" and "layer" property for the renderer-specific property list and
// hide the node for this renderer.
// ATTENTION: This is required, since the synchronized global property needs to be overwritten
// to make sure that the visibility is correctly set for the specific base renderer.
this->DeselectNode(node); // item d) and e)
}
// else: item c): node that was already selected before does not need to be modified
}
}
void QmitkSynchronizedNodeSelectionWidget::ReinitNode(const mitk::DataNode* dataNode)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
auto selectedImage = dynamic_cast<mitk::Image*>(dataNode->GetData());
if (nullptr == selectedImage)
{
return;
}
auto boundingBoxPredicate = mitk::NodePredicateNot::New(
mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer));
if (!boundingBoxPredicate->CheckNode(dataNode))
{
return;
}
mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), selectedImage->GetTimeGeometry());
}
void QmitkSynchronizedNodeSelectionWidget::RemoveFromInternalSelection(mitk::DataNode* dataNode)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
if (this->IsSynchronized())
{
// If the model is synchronized, all nodes use global / default properties.
// Thus we need to modify the global property of the node.
// Explicitly set the visibility to false for unselected nodes to hide them in the render window.
dataNode->SetVisibility(false);
}
m_Controls.selectionModeCheckBox->setChecked(false);
emit SelectionModeChanged(false);
this->RemoveNodeFromSelection(dataNode);
}
bool QmitkSynchronizedNodeSelectionWidget::IsParentNodeSelected(const mitk::DataNode* dataNode) const
{
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return false;
}
auto currentSelection = this->GetCurrentInternalSelection();
auto parentNodes = dataStorage->GetSources(dataNode, m_NodePredicate, false);
for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it)
{
const mitk::DataNode* parentNode = it->Value();
auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), parentNode);
if (finding != std::end(currentSelection)) // parent node found
{
// at least one parent node is part of the selection
return true;
}
}
return false;
}
void QmitkSynchronizedNodeSelectionWidget::DeselectNode(mitk::DataNode* dataNode)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
if (nullptr == dataNode)
{
return;
}
if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode))
{
// If the node should not be part of the selection, set the relevant renderer-specific properties.
// This will set the "visible" and "layer" property for the renderer-specific property list,
// such that the global / default property list values are overwritten.
mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, baseRenderer);
// Explicitly set the visibility to false for the node to hide them in the render window.
dataNode->SetVisibility(false, baseRenderer);
}
}
void QmitkSynchronizedNodeSelectionWidget::SelectAll()
{
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
auto allNodes = m_NodePredicate ? dataStorage->GetSubset(m_NodePredicate) : dataStorage->GetAll();
NodeList currentSelection;
for (auto& node : *allNodes)
{
currentSelection.append(node);
}
this->HandleChangeOfInternalSelection(currentSelection);
}
diff --git a/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp b/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp
index bf9590c68f..bc57e65258 100644
--- a/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp
+++ b/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp
@@ -1,619 +1,619 @@
/*============================================================================
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 "QmitkAbstractNodeSelectionWidget.h"
#include <QApplication>
#include <mitkNodePredicateFunction.h>
#include <mitkStandaloneDataStorage.h>
#include "QmitkModelViewSelectionConnector.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkRenderingTestHelper.h>
extern std::vector<std::string> globalCmdLineArgs;
class TestWidget : public QmitkAbstractNodeSelectionWidget
{
public:
explicit TestWidget(QWidget* parent = nullptr) : QmitkAbstractNodeSelectionWidget(parent), m_UpdateInfo(0), m_OnNodePredicateChanged(0), m_OnDataStorageChanged(0),
m_OnInternalSelectionChanged(0), m_OnNodeAddedToStorage(0), m_OnNodeRemovedFromStorage(0), m_ReviseSelectionChanged(0), m_AllowEmissionOfSelection(0), m_Allow(true), m_NewSelectionEmited(0)
{
connect(this, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &TestWidget::NewSelectionEmited);
};
int m_UpdateInfo;
void UpdateInfo() override
{
m_UpdateInfo++;
};
int m_OnNodePredicateChanged;
void OnNodePredicateChanged() override
{
m_OnNodePredicateChanged++;
};
int m_OnDataStorageChanged;
void OnDataStorageChanged() override
{
m_OnDataStorageChanged++;
};
int m_OnInternalSelectionChanged;
void OnInternalSelectionChanged() override
{
m_OnInternalSelectionChanged++;
};
int m_OnNodeAddedToStorage;
void OnNodeAddedToStorage(const mitk::DataNode* /*node*/) override
{
m_OnNodeAddedToStorage++;
};
int m_OnNodeRemovedFromStorage;
void OnNodeRemovedFromStorage(const mitk::DataNode* /*node*/) override
{
m_OnNodeRemovedFromStorage++;
};
int m_ReviseSelectionChanged;
void ReviseSelectionChanged(const NodeList& /*oldInternalSelection*/, NodeList& /*newInternalSelection*/) override
{
m_ReviseSelectionChanged++;
};
mutable int m_AllowEmissionOfSelection;
bool m_Allow;
bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const override
{
m_AllowEmissionOfSelection++;
if (m_Allow) return QmitkAbstractNodeSelectionWidget::AllowEmissionOfSelection(emissionCandidates);
return false;
};
int m_NewSelectionEmited;
QmitkAbstractNodeSelectionWidget::NodeList m_LastNewEmision;
void NewSelectionEmited(NodeList selection)
{
m_NewSelectionEmited++;
m_LastNewEmision = selection;
};
};
class QmitkAbstractNodeSelectionWidgetTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(QmitkAbstractNodeSelectionWidgetTestSuite);
MITK_TEST(SetDataStorageTest);
MITK_TEST(DataStorageEventTest);
MITK_TEST(NodePredicateTest);
MITK_TEST(SelectOnlyVisibleNodesTest);
MITK_TEST(OtherSetterAndGetterTest);
MITK_TEST(AllowEmissionOfSelectionTest);
MITK_TEST(OnNodeModifiedTest);
MITK_TEST(SignalRecursionTest);
CPPUNIT_TEST_SUITE_END();
mitk::DataStorage::Pointer m_DataStorage;
mitk::DataNode::Pointer m_Node1;
mitk::DataNode::Pointer m_Node1_2;
mitk::DataNode::Pointer m_Node2;
mitk::DataNode::Pointer m_Node3;
QApplication* m_TestApp;
public:
void setUp() override
{
m_DataStorage = mitk::StandaloneDataStorage::New();
m_Node1 = mitk::DataNode::New();
m_Node1->SetName("node1_1");
m_Node2 = mitk::DataNode::New();
m_Node2->SetName("node2");
m_Node3 = mitk::DataNode::New();
m_Node3->SetName("node3");
m_Node1_2 = mitk::DataNode::New();
m_Node1_2->SetName("node1_2");
m_DataStorage->Add(m_Node1);
m_DataStorage->Add(m_Node2);
m_DataStorage->Add(m_Node3);
m_DataStorage->Add(m_Node1_2);
mitk::RenderingTestHelper::ArgcHelperClass cmdLineArgs(globalCmdLineArgs);
auto argc = cmdLineArgs.GetArgc();
auto argv = cmdLineArgs.GetArgv();
m_TestApp = new QApplication(argc, argv);
}
mitk::NodePredicateBase::Pointer GeneratTestPredicate(const std::string& name)
{
auto check = [name](const mitk::DataNode * node)
{
return node->GetName().find(name,0) == 0;
};
auto predicate = mitk::NodePredicateFunction::New(check);
return predicate.GetPointer();
}
void tearDown() override
{
delete m_TestApp;
}
void SetDataStorageTest()
{
TestWidget widget;
widget.SetDataStorage(nullptr);
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 0, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetDataStorage(m_DataStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetCurrentSelection({ m_Node1 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
widget.SetDataStorage(m_DataStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
widget.SetDataStorage(nullptr);
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 2, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
}
void DataStorageEventTest()
{
TestWidget widget;
auto newNode = mitk::DataNode::New();
widget.SetDataStorage(m_DataStorage);
m_DataStorage->Add(newNode);
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetCurrentSelection({ newNode });
m_DataStorage->Remove(m_Node1);
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ newNode }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ newNode }, widget.GetSelectedNodes()));
m_DataStorage->Remove(newNode);
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
widget.SetCurrentSelection({ m_Node2 });
m_DataStorage = nullptr;
CPPUNIT_ASSERT_EQUAL(4, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(4, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(4, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
}
void NodePredicateTest()
{
TestWidget widget;
CPPUNIT_ASSERT(nullptr == widget.GetNodePredicate());
auto testPred = GeneratTestPredicate("node2");
widget.SetNodePredicate(testPred);
CPPUNIT_ASSERT(testPred == widget.GetNodePredicate());
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
widget.SetCurrentSelection({ m_Node2 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node2 }, widget.GetSelectedNodes()));
//change predicate while nodes are selected
widget.SetNodePredicate(GeneratTestPredicate("node1"));
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
//change selection to mixed one (valid nodes and invalid ones)
widget.SetCurrentSelection({ m_Node1, m_Node2, m_Node1_2 });
CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(3, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(3, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(3, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(3, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.GetSelectedNodes()));
}
void SelectOnlyVisibleNodesTest()
{
TestWidget widget;
CPPUNIT_ASSERT_EQUAL(true, widget.GetSelectOnlyVisibleNodes());
CPPUNIT_ASSERT(nullptr == widget.GetNodePredicate());
auto testPred = GeneratTestPredicate("node2");
widget.SetNodePredicate(testPred);
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
widget.SetSelectOnlyVisibleNodes(false);
CPPUNIT_ASSERT_EQUAL(false, widget.GetSelectOnlyVisibleNodes());
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//change selection to mixed one (valid nodes and invalid ones)
widget.SetCurrentSelection({ m_Node1, m_Node2, m_Node1_2 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.GetSelectedNodes()));
//change predicate while nodes are selected
widget.SetNodePredicate(GeneratTestPredicate("node1"));
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(3, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.GetSelectedNodes()));
widget.SetSelectOnlyVisibleNodes(true);
CPPUNIT_ASSERT_EQUAL(true, widget.GetSelectOnlyVisibleNodes());
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(3, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.GetSelectedNodes()));
}
void OtherSetterAndGetterTest()
{
TestWidget widget;
CPPUNIT_ASSERT("Error. Select data." == widget.GetInvalidInfo());
CPPUNIT_ASSERT("Empty. Make a selection." == widget.GetEmptyInfo());
CPPUNIT_ASSERT("Select a data node" == widget.GetPopUpTitel());
CPPUNIT_ASSERT("" == widget.GetPopUpHint());
CPPUNIT_ASSERT(false == widget.GetSelectionIsOptional());
widget.SetInvalidInfo("SetInvalidInfo");
widget.SetEmptyInfo("SetEmptyInfo");
widget.SetPopUpTitel("SetPopUpTitel");
widget.SetPopUpHint("SetPopUpHint");
widget.SetSelectionIsOptional(true);
CPPUNIT_ASSERT("SetInvalidInfo" == widget.GetInvalidInfo());
CPPUNIT_ASSERT("SetEmptyInfo" == widget.GetEmptyInfo());
CPPUNIT_ASSERT("SetPopUpTitel" == widget.GetPopUpTitel());
CPPUNIT_ASSERT("SetPopUpHint" == widget.GetPopUpHint());
CPPUNIT_ASSERT(true == widget.GetSelectionIsOptional());
}
void AllowEmissionOfSelectionTest()
{
TestWidget widget;
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
widget.m_Allow = false;
widget.SetCurrentSelection({ m_Node1 });
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
}
void OnNodeModifiedTest()
{
TestWidget widget;
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
- //Check OnNodeModified behavour without predicate
+ //Check OnNodeModified behaviour without predicate
m_Node3->SetName("NewName");
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//simulate behaviour if allowance is changed to true
widget.m_Allow = false;
m_Node3->SetName("NewName_temp");
widget.m_Allow = true;
m_Node3->SetName("NewName");
CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(6, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//change irrelevant node
m_Node2->SetName("IrrelevantNode");
CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(6, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//test with predicate set
widget.SetNodePredicate(GeneratTestPredicate("node1"));
m_Node3->SetName("NowAlsoIrrelevantNode");
CPPUNIT_ASSERT_EQUAL(4, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(7, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ }, widget.GetSelectedNodes()));
widget.SetCurrentSelection({ m_Node1, m_Node1_2 });
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.m_LastNewEmision));
m_Node1->SetName("NodeSortedOutByPredicate");
CPPUNIT_ASSERT_EQUAL(6, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(4, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(9, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(4, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1_2 }, widget.GetSelectedNodes()));
}
void SignalRecursionTest()
{
TestWidget widget;
widget.SetDataStorage(m_DataStorage);
TestWidget widget2;
widget2.SetDataStorage(m_DataStorage);
QmitkAbstractNodeSelectionWidget::connect(&widget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, &widget2, &QmitkAbstractNodeSelectionWidget::SetCurrentSelection);
QmitkAbstractNodeSelectionWidget::connect(&widget2, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, &widget, &QmitkAbstractNodeSelectionWidget::SetCurrentSelection);
widget.SetCurrentSelection({ m_Node1 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
CPPUNIT_ASSERT_EQUAL(1, widget2.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget2.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget2.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget2.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget2.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget2.GetSelectedNodes()));
}
};
MITK_TEST_SUITE_REGISTRATION(QmitkAbstractNodeSelectionWidget)
diff --git a/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp b/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp
index 87430b2647..976be30968 100644
--- a/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp
+++ b/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp
@@ -1,108 +1,108 @@
/*============================================================================
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 "mitkCommon.h"
#include "mitkTestingMacros.h"
#include <itksys/SystemTools.hxx>
#include <mitkLogBackend.h>
#include <mitkStandardFileLocations.h>
#include <string>
#include <QtConcurrentRun>
void LogMessages(unsigned int threadID, unsigned int numberOfTimes)
{
unsigned int times = 0;
while (times < numberOfTimes)
{
MITK_INFO << "Test info stream in thread" << threadID << "\n even with newlines";
MITK_WARN << "Test warning stream in thread " << threadID << ". "
<< "Even with a very long text, even without meaning or implied meaning or content, just a long sentence "
"to see whether something has problems with long sentences or output in files or into windows or "
"commandlines or whatever.";
MITK_DEBUG << "Test debugging stream in thread " << threadID;
MITK_ERROR << "Test error stream in thread " << threadID;
MITK_FATAL << "Test fatal stream in thread " << threadID;
times += 5;
}
}
/**
\brief Test logging from Qt threads
*/
static void TestThreadSaveLog(bool toFile)
{
bool testSucceded = true;
try
{
if (toFile)
{
std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/qtestthreadlog.log";
itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files
mitk::LogBackend::SetLogFile(filename);
}
unsigned int numberOfThreads = 10;
unsigned int threadRuntimeInMilliseconds = 4000;
QVector<QFuture<void>> threads;
// Spawn some threads
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
threads.push_back(QtConcurrent::run(LogMessages, threadIdx, 100));
std::cout << "Created " << threadIdx << ". thread." << std::endl;
}
// wait for some time (milliseconds)
itksys::SystemTools::Delay(threadRuntimeInMilliseconds);
// Wait for all to finish
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
threads[threadIdx].waitForFinished();
std::cout << threadIdx << ". thread has finished" << std::endl;
}
}
catch (std::exception e)
{
MITK_ERROR << "exception during 'TestThreadSaveLog': " << e.what();
testSucceded = false;
}
catch (...)
{
MITK_ERROR << "unknown exception during 'TestThreadSaveLog'";
testSucceded = false;
}
- // if no error occured until now, everything is ok
+ // if no error occurred until now, everything is ok
MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging in different threads.");
}
int QmitkThreadedLogTest(int /* argc */, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("QmitkThreadedLogTest")
- MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!")
+ MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURRED!")
TestThreadSaveLog(false); // false = to console
TestThreadSaveLog(true); // true = to file
MITK_TEST_OUTPUT(<< "Number of threads in QThreadPool: " << QThreadPool::globalInstance()->maxThreadCount())
// always end with this!
MITK_TEST_END()
}
diff --git a/Modules/QtWidgetsExt/include/QmitkFileChooser.h b/Modules/QtWidgetsExt/include/QmitkFileChooser.h
index 17e8ef219f..aa1b6c5105 100644
--- a/Modules/QtWidgetsExt/include/QmitkFileChooser.h
+++ b/Modules/QtWidgetsExt/include/QmitkFileChooser.h
@@ -1,114 +1,114 @@
/*============================================================================
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 QmitkFileChooser_h
#define QmitkFileChooser_h
#include "MitkQtWidgetsExtExports.h"
#include <QWidget>
class QPushButton;
class QLineEdit;
///
/// \brief Convenience Widget showing a line edit with the path of
/// a file or directory and a button which invokes a file choose dialog
///
/// Various methods are given to influence the behaviour or presentation
///
class MITKQTWIDGETSEXT_EXPORT QmitkFileChooser : public QWidget
{
Q_OBJECT
public:
///
/// standard ctor, init values to defaults (see methods for values)
///
explicit QmitkFileChooser(QWidget *parent = nullptr, Qt::WindowFlags f = {});
///
/// determines whether the button "Select file" is shown on the left
/// underneath the line edit, default=false (=is shown in a vertical layout)
///
void SetHorizotalLayout(bool horizontalLayout);
///
/// determines whether the selection must be a directory
/// default=false
///
void SetSelectDir(bool selectDir);
///
/// determines whether the file/directory mustexist
/// default=true
///
void SetFileMustExist(bool fileMustExist);
///
/// sets the file input, default=""
///
void SetFile(const std::string &file);
///
/// sets a file pattern to be selected, default=""
///
void SetFilePattern(const std::string &filepattern);
///
/// sets whether the user can edit the input, default=false
///
void SetReadOnly(bool ReadOnly);
///
/// returns whether the selected file/directory exists
///
bool IsValidFile() const;
///
/// returns the currently set file (an absolute path)
///
virtual std::string GetFile() const;
signals:
///
- /// emitted when the input changed programatically or by the user
+ /// emitted when the input changed programmatically or by the user
///
void NewFileSelected(const std::string &);
protected slots:
///
/// show dialog here
///
virtual void OnSelectFileClicked(bool /*checked=false*/);
///
/// check for valid here
///
virtual void OnFileEditingFinished();
protected:
///
/// \see SetSelectDir()
///
bool m_SelectDir;
///
/// \see SetFileMustExist()
///
bool m_FileMustExist;
///
/// \see SetFilePattern()
///
QString m_FilePattern;
///
/// the select file button
///
QPushButton *m_SelectFile;
///
/// the line edit to show the current file
///
QLineEdit *m_File;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h b/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h
index 3d2acc85ea..633910c115 100644
--- a/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h
+++ b/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h
@@ -1,83 +1,83 @@
/*============================================================================
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 QmitkNumberPropertyEditor_h
#define QmitkNumberPropertyEditor_h
#include "MitkQtWidgetsExtExports.h"
#include <QSpinBox>
#include <mitkProperties.h>
#include <mitkPropertyObserver.h>
/// @ingroup Widgets
class MITKQTWIDGETSEXT_EXPORT QmitkNumberPropertyEditor : public QSpinBox, public mitk::PropertyEditor
{
Q_OBJECT
Q_PROPERTY(short decimalPlaces READ getDecimalPlaces WRITE setDecimalPlaces)
Q_PROPERTY(bool showPercent READ getShowPercent WRITE setShowPercent)
Q_PROPERTY(int minValue READ minValue WRITE setMinValue)
Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue)
public:
QmitkNumberPropertyEditor(mitk::IntProperty *, QWidget *parent);
QmitkNumberPropertyEditor(mitk::FloatProperty *, QWidget *parent);
QmitkNumberPropertyEditor(mitk::DoubleProperty *, QWidget *parent);
~QmitkNumberPropertyEditor() override;
short getDecimalPlaces() const;
void setDecimalPlaces(short);
bool getShowPercent() const;
void setShowPercent(bool);
int minValue() const;
void setMinValue(int);
int maxValue() const;
void setMaxValue(int);
double doubleValue() const;
void setDoubleValue(double);
protected:
void initialize();
QString textFromValue(int) const override;
int valueFromText(const QString &) const override;
void PropertyChanged() override;
void PropertyRemoved() override;
void DisplayNumber();
union {
mitk::GenericProperty<int> *m_IntProperty;
mitk::GenericProperty<float> *m_FloatProperty;
mitk::GenericProperty<double> *m_DoubleProperty;
};
const int m_DataType;
short m_DecimalPlaces; // how many decimal places are shown
- double m_FactorPropertyToSpinbox; // internal conversion factor. neccessary because spinbox ranges work only with ints
- double m_FactorSpinboxToDisplay; // internal conversion factor. neccessary because spinbox ranges work only with ints
+ double m_FactorPropertyToSpinbox; // internal conversion factor. necessary because spinbox ranges work only with ints
+ double m_FactorSpinboxToDisplay; // internal conversion factor. necessary because spinbox ranges work only with ints
bool m_ShowPercents; // whether values are given in percent (0.5 -> 50%)
protected slots:
void onValueChanged(int);
private:
void adjustFactors(short, bool);
bool m_SelfChangeLock;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkPointListModel.h b/Modules/QtWidgetsExt/include/QmitkPointListModel.h
index d3826c637f..05cbc0692a 100644
--- a/Modules/QtWidgetsExt/include/QmitkPointListModel.h
+++ b/Modules/QtWidgetsExt/include/QmitkPointListModel.h
@@ -1,125 +1,125 @@
/*============================================================================
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 QmitkPointListModel_h
#define QmitkPointListModel_h
#include "MitkQtWidgetsExtExports.h"
#include <QAbstractListModel>
#include "mitkDataNode.h"
#include "mitkPointSet.h"
class MITKQTWIDGETSEXT_EXPORT QmitkPointListModel : public QAbstractListModel
{
Q_OBJECT
public:
QmitkPointListModel(mitk::DataNode * = nullptr, int t = 0, QObject *parent = nullptr);
~QmitkPointListModel() override;
Qt::ItemFlags flags(const QModelIndex &) const override;
/// interface of QAbstractListModel
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/// interface of QAbstractListModel
QVariant data(const QModelIndex &index, int role) const override;
/// interface of QAbstractListModel
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
/// which point set to work on
void SetPointSetNode(mitk::DataNode *pointSetNode);
/// which point set to work on
mitk::PointSet *GetPointSet() const;
// which point set to work on
mitk::DataNode *GetPointSetNode() const;
/// which time step to display/model
void SetTimeStep(int t);
/// which time step to display/model
int GetTimeStep() const;
/// itk observer for point set "modified" events
void OnPointSetChanged(const itk::EventObject &e);
/// itk observer for point set "delete" events
void OnPointSetDeleted(const itk::EventObject &e);
/**
* \brief get point and point ID that correspond to a given QModelIndex
*
* The mitk::PointSet uses a map to store points in an ID<-->Point relation.
- * The IDs are not neccesarily continuously numbered, therefore, we can not
+ * The IDs are not necessarily continuously numbered, therefore, we can not
* directly use the QModelIndex as point ID. This method returns the point and
* the corresponding point id for a given QModelIndex. The point and the point ID
* are returned in the outgoing parameters p and id. If a valid point and ID were
* found, the method returns true, otherwise it returns false
* \param[in] index the index for which a point is requested.
The row() part of the index is used to find a corresponding point
* \param[out] p If a valid point is found, it will be stored in the p parameter
* \param[out] id If a valid point is found, the corresponding ID will be stored in id
* \return Returns true, if a valid point was found, false otherwise
*/
bool GetPointForModelIndex(const QModelIndex &index,
mitk::PointSet::PointType &p,
mitk::PointSet::PointIdentifier &id) const;
/**Documentation
* \brief returns a QModelIndex for a given point ID
*
* The mitk::PointSet uses a map to store points in an ID<-->Point relation.
- * The IDs are not neccesarily continuously numbered, therefore, we can not
+ * The IDs are not necessarily continuously numbered, therefore, we can not
* directly use the point ID as a QModelIndex. This method returns a QModelIndex
* for a given point ID in the outgoing parameter index.
* \param[in] id The point ID for which the QModelIndex will be created
* \param[out] index if a point with the ID id was found, index will contain a corresponding QModelIndex
* for that point
* \return returns true, if a valid QModelIndex was created, false otherwise
*/
bool GetModelIndexForPointID(mitk::PointSet::PointIdentifier id, QModelIndex &index) const;
public slots:
void MoveSelectedPointUp();
void MoveSelectedPointDown();
void RemoveSelectedPoint();
signals:
/// emitted, when views should update their selection status
/// (because mouse interactions in render windows can change
/// the selection status of points)
void SignalUpdateSelection();
protected:
/// internally observe different point set
void ObserveNewPointSet(mitk::DataNode *pointSetNode);
// initially checks if there is a PointSet as data in the DataNode.
// returns PointSet if so and nullptr if other data is set to node
mitk::PointSet *CheckForPointSetInNode(mitk::DataNode *node) const;
protected:
mitk::DataNode *m_PointSetNode;
unsigned int m_PointSetModifiedObserverTag;
unsigned int m_PointSetDeletedObserverTag;
int m_TimeStep;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkPointListView.h b/Modules/QtWidgetsExt/include/QmitkPointListView.h
index f8eed5ff5b..667ea6745d 100644
--- a/Modules/QtWidgetsExt/include/QmitkPointListView.h
+++ b/Modules/QtWidgetsExt/include/QmitkPointListView.h
@@ -1,124 +1,124 @@
/*============================================================================
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 QmitkPointListView_h
#define QmitkPointListView_h
#include "MitkQtWidgetsExtExports.h"
#include <QmitkPointListModel.h>
#include <QLabel>
#include <QListView>
#include <mitkSliceNavigationController.h>
class QmitkAbstractMultiWidget;
/*!
* \brief GUI widget for handling mitk::PointSet
*
* Displays all the points in a mitk::PointSet graphically.
* Reacts automatically to changes in the PointSet's selection status.
* Updates PointSet's selection status when this list's selection changes.
*
* If a QmitkAbstractMultiWidget is assigned via SetMultiWidget(), the
* crosshair of the QmitkAbstractMultiWidget is moved to the currently selected
* point.
*
*/
class MITKQTWIDGETSEXT_EXPORT QmitkPointListView : public QListView
{
Q_OBJECT
public:
QmitkPointListView(QWidget *parent = nullptr);
~QmitkPointListView() override;
/// assign a point set for observation
void SetPointSetNode(mitk::DataNode *pointSetNode);
/// which point set to work on
const mitk::PointSet *GetPointSet() const;
/**
* \brief If Multiwidget is set, the crosshair is automatically centering to the selected point
* As an alternative, if you dont have a multiwidget, you can call SetSnc1, SetSnc2, SetSnc3 to set the
* SliceNavigationControllers directly to enable the focussing feature.
*/
void SetMultiWidget(QmitkAbstractMultiWidget* multiWidget);
/**
* \brief Return the QmitkAbstractMultiWidget that is used for updating the render window crosshair.
*/
QmitkAbstractMultiWidget* GetMultiWidget() const;
/**
* @brief Add a mitk::SliceNavigationController instance.
* @param snc The mitk::SliceNavigationController instance.
*
* This method adds \c snc to the set of slice navigation controllers which are
* used to navigate to the selected point.
*/
void AddSliceNavigationController(mitk::SliceNavigationController *snc);
/**
* @brief Remove a mitk::SliceNavigationController instance.
* @param snc The mitk::SliceNavigationController instance.
*
* This method removes \c snc from the set of slice navigation controllers which are
* used to navigate to the selected point.
*/
void RemoveSliceNavigationController(mitk::SliceNavigationController *snc);
signals:
- void SignalPointSelectionChanged(); ///< this signal is emmitted, if the selection of a point in the pointset is changed
+ void SignalPointSelectionChanged(); ///< this signal is emitted, if the selection of a point in the pointset is changed
void SignalTimeStepChanged(int);
protected slots:
/// Filtering double click event for editing point coordinates via a dialog
void OnPointDoubleClicked(const QModelIndex &index);
/// called when the point set data structure changes
void OnPointSetSelectionChanged();
/// called when the selection of the view widget changes
void OnListViewSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
/// open ContextMenu
void ctxMenu(const QPoint &pos);
/// Turn TimeStep Fading On/Off
void SetFading(bool onOff);
/// Delete all points in the list
void ClearPointList();
/// delete all points in the list in the current timestep
void ClearPointListTS();
protected:
void keyPressEvent(QKeyEvent *e) override; ///< react to F2, F3 and DEL keys
void wheelEvent(QWheelEvent *event) override; ///< change timestep of the current pointset by mouse wheel
std::set<mitk::SliceNavigationController *> m_Sncs;
QmitkPointListModel *m_PointListModel;
bool m_SelfCall;
bool m_showFading;
/// used to position the planes on a selected point
QmitkAbstractMultiWidget* m_MultiWidget;
};
#endif
diff --git a/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp b/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp
index dd4d4e1d08..da1a3b5d1c 100644
--- a/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp
@@ -1,271 +1,271 @@
/*============================================================================
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 "QmitkColorPropertyEditor.h"
#include <QApplication>
#include <QCloseEvent>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <mitkRenderingManager.h>
//----- QmitkPopupColorChooser ---------------------------------------------------------
QmitkPopupColorChooser::QmitkPopupColorChooser(QWidget *parent, unsigned int steps, unsigned int size)
: QFrame(parent, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool | Qt::X11BypassWindowManagerHint),
my_parent(parent)
{
setSteps(steps);
setLineWidth(2);
setMouseTracking(true);
setFrameStyle(QFrame::Panel | QFrame::Raised);
setLineWidth(1);
ensurePolished();
resize(size, size);
hide();
}
QmitkPopupColorChooser::~QmitkPopupColorChooser()
{
}
void QmitkPopupColorChooser::setSteps(int steps)
{
m_Steps = steps;
m_Steps2 = m_Steps / 2;
m_HStep = 360 / m_Steps;
m_SStep = 512 / m_Steps;
m_VStep = 512 / m_Steps;
}
void QmitkPopupColorChooser::keyReleaseEvent(QKeyEvent *)
{
emit colorSelected(m_OriginalColor);
close();
}
void QmitkPopupColorChooser::mouseMoveEvent(QMouseEvent *e)
{
double x(e->pos().x());
double y(e->pos().y());
x /= width();
if (x >= 0.0)
{
x = (int)(x * (float)(m_Steps - 1)) / (float)(m_Steps - 1);
if (x > 1.0)
x = 1.0;
if (x < 0.0)
x = 0.0;
}
y /= height();
if (y >= 1.0)
y = 0.9;
if (y < 0.0)
y = 0.0;
y = (int)(y * (float)m_Steps) / (float)m_Steps;
m_H = static_cast<int>(y * 359.0);
if (x >= 0.5)
{
m_S = static_cast<int>((1.0 - x) * 511.0);
if (m_S > 255)
m_S = 255;
m_V = 255;
}
else
{
m_S = 255;
if (x < 0.0)
m_V = 0;
else
{
m_V = static_cast<int>(x * 511.0 + 511.0 / (float)(m_Steps - 1));
if (m_V > 255)
m_V = 255;
}
}
QColor color;
color.setHsv(m_H, m_S, m_V);
emit colorSelected(color);
}
void QmitkPopupColorChooser::mouseReleaseEvent(QMouseEvent *)
{
close();
}
void QmitkPopupColorChooser::closeEvent(QCloseEvent *e)
{
e->accept();
releaseKeyboard();
releaseMouse();
if (!m_popupParent)
return;
- // remember that we (as a popup) might recieve the mouse release
+ // remember that we (as a popup) might receive the mouse release
// event instead of the popupParent. This is due to the fact that
// the popupParent popped us up in its mousePressEvent handler. To
// avoid the button remaining in pressed state we simply send a
// faked mouse button release event to it.
// Maleike: parent should not pop us on MouseRelease!
QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0, 0), QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QApplication::sendEvent(m_popupParent, &me);
}
void QmitkPopupColorChooser::popup(QWidget *parent, const QPoint &point, const mitk::Color *color)
{
m_popupParent = parent;
if (m_popupParent)
{
QPoint newPos;
if (color)
{
QColor qcolor((int)((*color)[0] * 255.0), (int)((*color)[1] * 255.0), (int)((*color)[2] * 255.0));
int h, s, v;
qcolor.getHsv(&h, &s, &v);
if (h == -1) // set by Qt if color is achromatic ( but this widget does not display grays )
h = 10; // red
int x, y;
float cellwidth = (float)width() / (float)(m_Steps);
if (s > v) // restrict to the colors we can display
{ // left side, ramp from v = 255/m_Steps to v = 255
s = 255;
x = (int)((((float)v / 255.0) * ((float)m_Steps2) - 1.0) * cellwidth + cellwidth / 2);
}
else
{
v = 255;
x = (int)(((1.0 - ((float)s / 255.0)) * ((float)m_Steps2)) * cellwidth + cellwidth / 2
+
width() / 2);
}
y = (int)((float)h / 360.0 * (float)m_Steps * cellwidth);
m_OriginalColor.setHsv(h, s, v);
// move to color
newPos.setX(point.x() - x);
newPos.setY(point.y() - y);
}
else
{
// center widget
m_OriginalColor.setHsv(-1, 0, 0);
newPos.setX(point.x() - width() / 2);
newPos.setY(point.y() - height() / 2);
}
move(m_popupParent->mapToGlobal(newPos));
}
show();
raise();
grabMouse();
grabKeyboard();
}
void QmitkPopupColorChooser::paintEvent(QPaintEvent *)
{
QPainter painter(this);
drawGradient(&painter);
}
void QmitkPopupColorChooser::drawGradient(QPainter *p)
{
p->setWindow(0, 0, m_Steps - 1, m_Steps); // defines coordinate system
p->setPen(Qt::NoPen);
QColor c;
for (unsigned int h = 0; h < m_Steps; ++h)
{
for (unsigned int v = 1; v < m_Steps2; ++v)
{
c.setHsv(h * m_HStep, 255, v * m_VStep); // rainbow effect
p->setBrush(c); // solid fill with color c
p->drawRect(v - 1, h, m_Steps2, m_Steps); // draw the rectangle
}
for (unsigned int s = 0; s < m_Steps2; ++s)
{
c.setHsv(h * m_HStep, 255 - s * m_SStep, 255); // rainbow effect
p->setBrush(c); // solid fill with color c
p->drawRect(m_Steps2 + s - 1, h, m_Steps2, m_Steps); // draw the rectangle
}
}
}
//----- QmitkColorPropertyEditor --------------------------------------------------
// initialization of static pointer to color picker widget
QmitkPopupColorChooser *QmitkColorPropertyEditor::colorChooser = nullptr;
int QmitkColorPropertyEditor::colorChooserRefCount = 0;
QmitkColorPropertyEditor::QmitkColorPropertyEditor(const mitk::ColorProperty *property, QWidget *parent)
: QmitkColorPropertyView(property, parent)
{
if (colorChooserRefCount == 0)
{
colorChooser = new QmitkPopupColorChooser(nullptr, 50);
}
++colorChooserRefCount;
}
QmitkColorPropertyEditor::~QmitkColorPropertyEditor()
{
--colorChooserRefCount;
if (!colorChooserRefCount)
{
delete colorChooser;
colorChooser = nullptr;
}
}
void QmitkColorPropertyEditor::mousePressEvent(QMouseEvent *e)
{
connect(colorChooser, SIGNAL(colorSelected(QColor)), this, SLOT(onColorSelected(QColor)));
if (m_ColorProperty)
{
colorChooser->popup(this, e->pos(), &(m_ColorProperty->GetColor()));
}
}
void QmitkColorPropertyEditor::mouseReleaseEvent(QMouseEvent *)
{
disconnect(colorChooser, SIGNAL(colorSelected(QColor)), this, SLOT(onColorSelected(QColor)));
}
void QmitkColorPropertyEditor::onColorSelected(QColor c)
{
if (m_ColorProperty)
{
int r, g, b;
c.getRgb(&r, &g, &b);
const_cast<mitk::ColorProperty *>(m_ColorProperty)->SetColor(r / 255.0, g / 255.0, b / 255.0);
m_ColorProperty->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
diff --git a/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp b/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp
index 62407ef96a..b294b50b33 100644
--- a/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp
@@ -1,51 +1,51 @@
/*============================================================================
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 "QmitkColorPropertyView.h"
#include <QPixmap>
#define ROUND_P(x) ((int)((x) + 0.5))
QmitkColorPropertyView::QmitkColorPropertyView(const mitk::ColorProperty *property, QWidget *parent)
: QLabel(parent), PropertyView(property), m_ColorProperty(property)
{
- setText(" "); // two spaces for some minimun height
+ setText(" "); // two spaces for some minimum height
setMinimumSize(15, 15);
PropertyChanged();
m_WidgetPalette = QWidget::palette();
QWidget::setPalette(m_WidgetPalette);
QWidget::setAutoFillBackground(true);
}
QmitkColorPropertyView::~QmitkColorPropertyView()
{
}
void QmitkColorPropertyView::PropertyChanged()
{
if (m_Property)
DisplayColor();
}
void QmitkColorPropertyView::PropertyRemoved()
{
m_Property = nullptr;
m_ColorProperty = nullptr;
}
void QmitkColorPropertyView::DisplayColor()
{
const mitk::Color &tmp_col(m_ColorProperty->GetColor());
QColor color(ROUND_P(tmp_col[0] * 255.0), ROUND_P(tmp_col[1] * 255.0), ROUND_P(tmp_col[2] * 255.0));
m_WidgetPalette.setColor(QPalette::Window, color);
}
diff --git a/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp b/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp
index 11d5bfe122..07ef5678d1 100644
--- a/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp
@@ -1,341 +1,341 @@
/*============================================================================
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 <QmitkNumberPropertySlider.h>
#include <mitkProperties.h>
#include <mitkPropertyObserver.h>
#include <mitkRenderingManager.h>
#define DT_SHORT 1
#define DT_INT 2
#define DT_FLOAT 3
#define DT_DOUBLE 4
#define ROUND(x) (((x) > 0) ? int((x) + 0.5) : int((x)-0.5))
#define ROUND_SHORT(x) (((x) > 0) ? short((x) + 0.5) : short((x)-0.5))
class QmitkNumberPropertySlider::Impl
{
public:
Impl(QmitkNumberPropertySlider *q);
void DisplayNumber();
void adjustFactors(short, bool);
class Editor : public mitk::PropertyEditor
{
public:
Editor(mitk::IntProperty *, Impl *impl);
Editor(mitk::FloatProperty *, Impl *impl);
Editor(mitk::DoubleProperty *, Impl *impl);
void PropertyChanged() override;
void PropertyRemoved() override;
void BeginModifyProperty() { mitk::PropertyEditor::BeginModifyProperty(); }
void EndModifyProperty() { mitk::PropertyEditor::EndModifyProperty(); }
union {
mitk::GenericProperty<int> *m_IntProperty;
mitk::GenericProperty<float> *m_FloatProperty;
mitk::GenericProperty<double> *m_DoubleProperty;
};
const int m_DataType;
private:
Impl *m_Impl;
};
std::unique_ptr<Editor> m_PropEditor;
short m_DecimalPlaces; // how many decimal places are shown
- double m_FactorPropertyToSlider; // internal conversion factor. neccessary because slider ranges work only with ints
- double m_FactorSliderToDisplay; // internal conversion factor. neccessary because slider ranges work only with ints
+ double m_FactorPropertyToSlider; // internal conversion factor. necessary because slider ranges work only with ints
+ double m_FactorSliderToDisplay; // internal conversion factor. necessary because slider ranges work only with ints
bool m_ShowPercents; // whether values are given in percent (0.5 -> 50%)
bool m_SelfChangeLock;
private:
void initialize();
QmitkNumberPropertySlider *q;
};
QmitkNumberPropertySlider::Impl::Editor::Editor(mitk::IntProperty *property, Impl *impl)
: mitk::PropertyEditor(property), m_IntProperty(property), m_DataType(DT_INT), m_Impl(impl)
{
}
QmitkNumberPropertySlider::Impl::Editor::Editor(mitk::FloatProperty *property, Impl *impl)
: mitk::PropertyEditor(property), m_FloatProperty(property), m_DataType(DT_FLOAT), m_Impl(impl)
{
}
QmitkNumberPropertySlider::Impl::Editor::Editor(mitk::DoubleProperty *property, Impl *impl)
: mitk::PropertyEditor(property), m_DoubleProperty(property), m_DataType(DT_DOUBLE), m_Impl(impl)
{
}
QmitkNumberPropertySlider::~QmitkNumberPropertySlider()
{
}
void QmitkNumberPropertySlider::SetProperty(mitk::IntProperty *property)
{
if (property == nullptr)
{
d->m_PropEditor.reset();
this->setEnabled(false);
}
else
{
d->m_PropEditor.reset(new Impl::Editor(property, d.get()));
d->m_PropEditor->PropertyChanged();
this->setEnabled(true);
}
}
void QmitkNumberPropertySlider::SetProperty(mitk::FloatProperty *property)
{
if (property == nullptr)
{
d->m_PropEditor.reset();
this->setEnabled(false);
}
else
{
d->m_PropEditor.reset(new Impl::Editor(property, d.get()));
d->m_PropEditor->PropertyChanged();
this->setEnabled(true);
}
}
void QmitkNumberPropertySlider::SetProperty(mitk::DoubleProperty *property)
{
if (property == nullptr)
{
d->m_PropEditor.reset();
this->setEnabled(false);
}
else
{
d->m_PropEditor.reset(new Impl::Editor(property, d.get()));
d->m_PropEditor->PropertyChanged();
this->setEnabled(true);
}
}
QmitkNumberPropertySlider::Impl::Impl(QmitkNumberPropertySlider *q)
: m_DecimalPlaces(0),
m_FactorPropertyToSlider(1.0),
m_FactorSliderToDisplay(1.0),
m_ShowPercents(false),
m_SelfChangeLock(false),
q(q)
{
}
QmitkNumberPropertySlider::QmitkNumberPropertySlider(QWidget *parent) : QSlider(parent), d(new Impl(this))
{
connect(this, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int)));
this->setEnabled(false);
}
void QmitkNumberPropertySlider::Impl::adjustFactors(short newDecimalPlaces, bool newShowPercents)
{
int oldMax = q->maxValue();
int oldMin = q->minValue();
m_DecimalPlaces = newDecimalPlaces;
m_ShowPercents = newShowPercents;
m_FactorPropertyToSlider = pow(10.0, m_DecimalPlaces);
m_FactorSliderToDisplay = 1.0 / m_FactorPropertyToSlider;
if (m_ShowPercents)
m_FactorPropertyToSlider *= 100.0;
q->setMinimum(oldMin);
q->setMaximum(oldMax);
}
short QmitkNumberPropertySlider::getDecimalPlaces() const
{
return d->m_DecimalPlaces;
}
void QmitkNumberPropertySlider::setDecimalPlaces(short places)
{
if (d->m_PropEditor.get() == nullptr)
return;
switch (d->m_PropEditor->m_DataType)
{
case DT_FLOAT:
case DT_DOUBLE:
{
d->adjustFactors(places, d->m_ShowPercents);
d->DisplayNumber();
break;
}
default:
break;
}
}
bool QmitkNumberPropertySlider::getShowPercent() const
{
return d->m_ShowPercents;
}
void QmitkNumberPropertySlider::setShowPercent(bool showPercent)
{
if (showPercent == d->m_ShowPercents)
return; // nothing to change
if (d->m_PropEditor.get() == nullptr)
return;
switch (d->m_PropEditor->m_DataType)
{
case DT_FLOAT:
case DT_DOUBLE:
{
d->adjustFactors(d->m_DecimalPlaces, showPercent);
break;
}
default:
{
break;
}
}
d->DisplayNumber();
}
int QmitkNumberPropertySlider::minValue() const
{
return ROUND(QSlider::minimum() / d->m_FactorPropertyToSlider);
}
void QmitkNumberPropertySlider::setMinValue(int value)
{
QSlider::setMinimum(ROUND(value * d->m_FactorPropertyToSlider));
}
int QmitkNumberPropertySlider::maxValue() const
{
return ROUND(QSlider::maximum() / d->m_FactorPropertyToSlider);
}
void QmitkNumberPropertySlider::setMaxValue(int value)
{
QSlider::setMaximum(ROUND(value * d->m_FactorPropertyToSlider));
}
double QmitkNumberPropertySlider::doubleValue() const
{
return static_cast<double>((QSlider::value()) / d->m_FactorPropertyToSlider);
}
void QmitkNumberPropertySlider::setDoubleValue(double value)
{
QSlider::setValue(ROUND(value * d->m_FactorPropertyToSlider));
}
void QmitkNumberPropertySlider::onValueChanged(int value)
{
if (d->m_PropEditor.get() == nullptr)
return;
if (d->m_SelfChangeLock)
return; // valueChanged is even emitted, when this widget initiates a change to its value
// this may be useful some times, but in this use, it would be wrong:
// (A) is an editor with 3 decimal places
// (B) is an editor with 2 decimal places
// User changes A's displayed value to 4.002
// A's onValueChanged gets called, sets the associated Property to 4.002
// B's onPropertyChanged gets called, sets its display to 4.00
// B's onValueChanged gets called and sets the associated Property to 4.00
// A's onPropertyChanged gets called, sets its display to 4.000
d->m_PropEditor->BeginModifyProperty();
double newValue(value / d->m_FactorPropertyToSlider);
switch (d->m_PropEditor->m_DataType)
{
case DT_INT:
{
d->m_PropEditor->m_IntProperty->SetValue(ROUND(newValue));
break;
}
case DT_FLOAT:
{
d->m_PropEditor->m_FloatProperty->SetValue(newValue);
break;
}
case DT_DOUBLE:
{
d->m_PropEditor->m_DoubleProperty->SetValue(newValue);
break;
}
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
d->m_PropEditor->EndModifyProperty();
}
void QmitkNumberPropertySlider::Impl::Editor::PropertyChanged()
{
m_Impl->DisplayNumber();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkNumberPropertySlider::Impl::Editor::PropertyRemoved()
{
this->m_Property = nullptr;
}
void QmitkNumberPropertySlider::Impl::DisplayNumber()
{
if (m_PropEditor.get() == nullptr)
return;
m_SelfChangeLock = true;
switch (m_PropEditor->m_DataType)
{
case DT_INT:
{
int i = m_PropEditor->m_IntProperty->GetValue();
q->setValue(i);
break;
}
case DT_FLOAT:
{
float f = m_PropEditor->m_FloatProperty->GetValue();
q->setDoubleValue(f);
break;
}
case DT_DOUBLE:
{
double d = m_PropEditor->m_DoubleProperty->GetValue();
q->setDoubleValue(d);
break;
}
default:
break;
}
m_SelfChangeLock = false;
}
diff --git a/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp b/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp
index 7e68f1c758..9efbd2cc24 100755
--- a/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp
@@ -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.
============================================================================*/
#include "QmitkTransferFunctionCanvas.h"
#include <itkObject.h>
#include <QColorDialog>
#include <QMouseEvent>
#include <QPainter>
QmitkTransferFunctionCanvas::QmitkTransferFunctionCanvas(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f),
m_GrabbedHandle(-1),
m_Lower(0.0),
m_Upper(1.0),
m_Min(0.0),
m_Max(1.0),
m_Histogram(nullptr),
m_ImmediateUpdate(false),
m_Range(0.0f),
m_LineEditAvailable(false),
m_XEdit(nullptr),
m_YEdit(nullptr)
{
setEnabled(false);
setFocusPolicy(Qt::ClickFocus);
}
void QmitkTransferFunctionCanvas::paintEvent(QPaintEvent *ev)
{
QWidget::paintEvent(ev);
}
std::pair<int, int> QmitkTransferFunctionCanvas::FunctionToCanvas(std::pair<double, double> functionPoint)
{
return std::make_pair(
(int)((functionPoint.first - m_Lower) / (m_Upper - m_Lower) * contentsRect().width()) + contentsRect().x(),
(int)(contentsRect().height() * (1 - functionPoint.second)) + contentsRect().y());
}
std::pair<double, double> QmitkTransferFunctionCanvas::CanvasToFunction(std::pair<int, int> canvasPoint)
{
return std::make_pair(
(canvasPoint.first - contentsRect().x()) * (m_Upper - m_Lower) / contentsRect().width() + m_Lower,
1.0 - (double)(canvasPoint.second - contentsRect().y()) / contentsRect().height());
}
void QmitkTransferFunctionCanvas::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
{
int nearHandle = GetNearHandle(mouseEvent->pos().x(), mouseEvent->pos().y());
if (nearHandle != -1)
{
this->DoubleClickOnHandle(nearHandle);
}
}
/** returns index of a near handle or -1 if none is near
*/
int QmitkTransferFunctionCanvas::GetNearHandle(int, int, unsigned int)
{
return -1;
}
void QmitkTransferFunctionCanvas::mousePressEvent(QMouseEvent *mouseEvent)
{
if (m_LineEditAvailable)
{
m_XEdit->clear();
if (m_YEdit)
m_YEdit->clear();
}
const auto pos = mouseEvent->position().toPoint();
m_GrabbedHandle = GetNearHandle(pos.x(), pos.y());
if ((mouseEvent->button() & Qt::LeftButton) && m_GrabbedHandle == -1)
{
auto [x, value] = this->CanvasToFunction(std::make_pair(pos.x(), pos.y()));
this->AddFunctionPoint(x, value);
m_GrabbedHandle = GetNearHandle(pos.x(), pos.y());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if ((mouseEvent->button() & Qt::RightButton) && m_GrabbedHandle != -1 && this->GetFunctionSize() > 1)
{
this->RemoveFunctionPoint(this->GetFunctionX(m_GrabbedHandle));
m_GrabbedHandle = -1;
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
update();
}
void QmitkTransferFunctionCanvas::mouseMoveEvent(QMouseEvent *mouseEvent)
{
if (m_GrabbedHandle != -1)
{
const auto pos = mouseEvent->position().toPoint();
std::pair<double, double> newPos = this->CanvasToFunction(std::make_pair(pos.x(), pos.y()));
// X Clamping
{
// Check with predecessor
if (m_GrabbedHandle > 0)
if (newPos.first <= this->GetFunctionX(m_GrabbedHandle - 1))
newPos.first = this->GetFunctionX(m_GrabbedHandle);
- // Check with sucessor
+ // Check with successor
if (m_GrabbedHandle < this->GetFunctionSize() - 1)
if (newPos.first >= this->GetFunctionX(m_GrabbedHandle + 1))
newPos.first = this->GetFunctionX(m_GrabbedHandle);
// Clamping to histogramm
if (newPos.first < m_Min)
newPos.first = m_Min;
else if (newPos.first > m_Max)
newPos.first = m_Max;
}
// Y Clamping
{
if (newPos.second < 0.0)
newPos.second = 0.0;
else if (newPos.second > 1.0)
newPos.second = 1.0;
}
// Move selected point
this->MoveFunctionPoint(m_GrabbedHandle, newPos);
update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionCanvas::mouseReleaseEvent(QMouseEvent *)
{
update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkTransferFunctionCanvas::PaintHistogram(QPainter &p)
{
if (m_Histogram)
{
p.save();
p.setPen(Qt::gray);
int displayWidth = contentsRect().width();
int displayHeight = contentsRect().height();
double windowLeft = m_Lower;
double windowRight = m_Upper;
double step = (windowRight - windowLeft) / double(displayWidth);
double pos = windowLeft;
for (int x = 0; x < displayWidth; x++)
{
double left = pos;
double right = pos + step;
float height = m_Histogram->GetRelativeBin(left, right);
if (height >= 0)
p.drawLine(x, displayHeight * (1 - height), x, displayHeight);
pos += step;
}
p.restore();
}
}
void QmitkTransferFunctionCanvas::keyPressEvent(QKeyEvent *e)
{
if (m_GrabbedHandle == -1)
return;
switch (e->key())
{
case Qt::Key_Delete:
if (this->GetFunctionSize() > 1)
{
this->RemoveFunctionPoint(GetFunctionX(m_GrabbedHandle));
m_GrabbedHandle = -1;
}
break;
case Qt::Key_Left:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle) - 1, GetFunctionY(m_GrabbedHandle))));
break;
case Qt::Key_Right:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle) + 1, GetFunctionY(m_GrabbedHandle))));
break;
case Qt::Key_Up:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle), GetFunctionY(m_GrabbedHandle) + 0.001)));
break;
case Qt::Key_Down:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle), GetFunctionY(m_GrabbedHandle) - 0.001)));
break;
}
update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
-// Update immediatly while changing the transfer function
+// Update immediately while changing the transfer function
void QmitkTransferFunctionCanvas::SetImmediateUpdate(bool state)
{
m_ImmediateUpdate = state;
}
diff --git a/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp b/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp
index cade3a0417..ab92c8bf42 100644
--- a/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp
@@ -1,288 +1,288 @@
/*============================================================================
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 "QmitkVideoBackground.h"
// MITK includes
#include "mitkRenderingManager.h"
#include "mitkVtkLayerController.h"
// QT includes
#include <QTimer>
// itk includes
#include <itkCommand.h>
// VTK includes
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCommand.h>
#include <vtkCommand.h>
#include <vtkImageActor.h>
#include <vtkImageImport.h>
#include <vtkMapper.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSystemIncludes.h>
QmitkVideoBackground::QmitkVideoBackground(QObject *parent)
: QObject(parent), m_QTimer(new QTimer(this)), m_VideoSource(nullptr), m_VideoSourceObserverTag(0)
{
this->ResetVideoBackground();
}
QmitkVideoBackground::QmitkVideoBackground(mitk::VideoSource *v, int)
: QObject(nullptr), m_QTimer(new QTimer(this)), m_VideoSource(nullptr), m_VideoSourceObserverTag(0)
{
this->SetVideoSource(v);
this->ResetVideoBackground();
}
void QmitkVideoBackground::ResetVideoBackground()
{
m_QTimer->setInterval(25);
connect(m_QTimer, SIGNAL(timeout()), SLOT(UpdateVideo()));
m_renderWindowVectorInfo.clear();
}
QmitkVideoBackground::~QmitkVideoBackground()
{
this->Disable();
}
void QmitkVideoBackground::AddRenderWindow(vtkRenderWindow *renderWindow)
{
if (!renderWindow || !m_VideoSource)
{
MITK_WARN << "No Renderwindow or VideoSource set!";
return;
}
this->RemoveRenderWindow(renderWindow);
vtkRenderer *videoRenderer = vtkRenderer::New();
vtkImageActor *videoActor = vtkImageActor::New();
vtkImageImport *videoImport = vtkImageImport::New();
videoImport->SetDataScalarTypeToUnsignedChar();
videoImport->SetNumberOfScalarComponents(3);
if (m_VideoSource->GetImageWidth() == 0)
m_VideoSource->FetchFrame();
videoImport->SetWholeExtent(0, m_VideoSource->GetImageWidth() - 1, 0, m_VideoSource->GetImageHeight() - 1, 0, 1 - 1);
videoImport->SetDataExtentToWholeExtent();
VideoBackgroundVectorInfo v;
v.renWin = renderWindow;
v.videoRenderer = videoRenderer;
v.videoActor = videoActor;
v.videoImport = videoImport;
// callback for the deletion of the renderwindow
vtkSmartPointer<vtkCallbackCommand> deleteCallback = vtkSmartPointer<vtkCallbackCommand>::New();
deleteCallback->SetCallback(QmitkVideoBackground::OnRenderWindowDelete);
deleteCallback->SetClientData(this);
v.renderWindowObserverTag = renderWindow->AddObserver(vtkCommand::DeleteEvent, deleteCallback);
m_renderWindowVectorInfo.push_back(v);
// completes the initialization
this->Modified();
}
void QmitkVideoBackground::RemoveRenderWindow(vtkRenderWindow *renderWindow)
{
this->RemoveRenderWindow(renderWindow, true);
}
void QmitkVideoBackground::RemoveRenderWindow(vtkRenderWindow *renderWindow, bool removeObserver)
{
// search for renderwindow and remove it
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
if ((*it).renWin == renderWindow)
{
mitk::VtkLayerController *layerController = mitk::VtkLayerController::GetInstance((*it).renWin);
- // unregister video backround renderer from renderwindow
+ // unregister video background renderer from renderwindow
if (layerController)
layerController->RemoveRenderer((*it).videoRenderer);
(*it).videoRenderer->Delete();
(*it).videoActor->Delete();
(*it).videoImport->Delete();
// remove listener
if (removeObserver)
renderWindow->RemoveObserver((*it).renderWindowObserverTag);
m_renderWindowVectorInfo.erase(it);
break;
}
}
}
bool QmitkVideoBackground::IsRenderWindowIncluded(vtkRenderWindow *renderWindow)
{
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
if ((*it).renWin == renderWindow)
return true;
}
return false;
}
void QmitkVideoBackground::Pause()
{
m_QTimer->stop();
}
void QmitkVideoBackground::Resume()
{
m_QTimer->start();
}
/**
* Enables drawing of the color Video background.
* If you want to disable it, call the Disable() function.
*/
void QmitkVideoBackground::Enable()
{
UpdateVideo();
Modified();
m_QTimer->start();
}
/**
* Disables drawing of the color Video background.
* If you want to enable it, call the Enable() function.
*/
void QmitkVideoBackground::Disable()
{
if (this->IsEnabled())
{
mitk::VtkLayerController *layerController = nullptr;
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
layerController = mitk::VtkLayerController::GetInstance((*it).renWin);
if (layerController)
layerController->RemoveRenderer((*it).videoRenderer);
}
m_QTimer->stop();
}
}
bool QmitkVideoBackground::IsEnabled()
{
return m_QTimer->isActive();
}
void QmitkVideoBackground::UpdateVideo()
{
if (m_renderWindowVectorInfo.size() > 0)
{
unsigned char *src = nullptr;
try
{
src = m_VideoSource->GetVideoTexture();
}
catch (const std::logic_error &error)
{
MITK_DEBUG << error.what();
emit EndOfVideoSourceReached(m_VideoSource);
return;
}
if (src)
{
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
(*it).videoImport->SetImportVoidPointer(src);
(*it).videoImport->Modified();
(*it).videoImport->Update();
mitk::RenderingManager::GetInstance()->RequestUpdate((*it).renWin);
}
emit NewFrameAvailable(m_VideoSource);
}
else
MITK_WARN << "No video texture available";
}
}
void QmitkVideoBackground::Modified()
{
- // ensures registration of video backrounds in each renderwindow
+ // ensures registration of video backgrounds in each renderwindow
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
(*it).videoImport->Update();
(*it).videoActor->SetInputData((*it).videoImport->GetOutput());
(*it).videoRenderer->AddActor2D((*it).videoActor);
(*it).videoRenderer->ResetCamera();
(*it).videoRenderer->InteractiveOff();
(*it).videoRenderer->GetActiveCamera()->ParallelProjectionOn();
(*it).videoRenderer->GetActiveCamera()->SetParallelScale(m_VideoSource->GetImageHeight() / 2);
mitk::VtkLayerController *layerController = mitk::VtkLayerController::GetInstance((*it).renWin);
if (layerController && !layerController->IsRendererInserted((*it).videoRenderer))
layerController->InsertBackgroundRenderer((*it).videoRenderer, true);
}
}
void QmitkVideoBackground::SetVideoSource(mitk::VideoSource *videoSource)
{
if (m_VideoSource == videoSource)
return;
if (m_VideoSource)
m_VideoSource->RemoveObserver(m_VideoSourceObserverTag);
m_VideoSource = videoSource;
if (m_VideoSource)
{
itk::MemberCommand<QmitkVideoBackground>::Pointer _ModifiedCommand =
itk::MemberCommand<QmitkVideoBackground>::New();
_ModifiedCommand->SetCallbackFunction(this, &QmitkVideoBackground::OnVideoSourceDelete);
m_VideoSourceObserverTag = m_VideoSource->AddObserver(itk::DeleteEvent(), _ModifiedCommand);
}
}
void QmitkVideoBackground::SetTimerDelay(int ms)
{
m_QTimer->setInterval(ms);
}
mitk::VideoSource *QmitkVideoBackground::GetVideoSource()
{
return m_VideoSource;
}
int QmitkVideoBackground::GetTimerDelay()
{
return m_QTimer->interval();
}
void QmitkVideoBackground::OnVideoSourceDelete(const itk::Object *, const itk::EventObject &)
{
this->Disable(); // will only disable if enabled
m_VideoSource = nullptr;
}
void QmitkVideoBackground::OnRenderWindowDelete(vtkObject *object, unsigned long, void *clientdata, void *)
{
QmitkVideoBackground *instance = static_cast<QmitkVideoBackground *>(clientdata);
instance->RemoveRenderWindow(static_cast<vtkRenderWindow *>(object), false);
}
diff --git a/Modules/REST/documentation/REST.dox b/Modules/REST/documentation/REST.dox
index 93cfd5a122..9f49f002a1 100644
--- a/Modules/REST/documentation/REST.dox
+++ b/Modules/REST/documentation/REST.dox
@@ -1,194 +1,194 @@
/**
\page RESTModule REST Module
\tableofcontents
\section REST_brief Description
The MITK REST Module is able to manage REST requests. The main class is the RESTManager.
It is a MicroServices which can be accessed via
\code{.cpp}
auto *context = us::GetModuleContext();
auto managerRef = context->GetServiceReference<IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
//call the function you need from the service
}
}
\endcode
\subsection REST_Technical Technical background
- The module uses the <a href="https://github.com/Microsoft/cpprestsdk">Microsoft C++ REST SDK</a> for REST mechanisms as well as JSON convertion and asynchronic programming.
+ The module uses the <a href="https://github.com/Microsoft/cpprestsdk">Microsoft C++ REST SDK</a> for REST mechanisms as well as JSON conversion and asynchronic programming.
\section Use_REST How to use the REST Module
You can use the REST module from two different perspectives in MITK:
<ol>
<li> The Server view (receive requests from clients)
<li> The Client view (send requests to servers)
</ol>
The following sections will give you an introduction on how to use which of those roles:
\subsection Server_Use Use from a Server perspective
To act as a server, you need to implement the <code>IRESTObserver</code>, which has a <code>Notify()</code> method that has to be implemented.
In this <code>Notify()</code> method you specify how you want to react to incoming requests and with which data you want to respond to the requests.
You can then start listening for requests from clients as shown below:
\code{.cpp}
auto *context = us::GetModuleContext();
auto managerRef = context->GetServiceReference<IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
managerService->ReceiveRequests(uri /*specify your uri which you want to receive requests for*/, this);
}
}
\endcode
If a client sends a request, the Notify method is called and a response is sent. By now, only GET-requests from clients are supported.
If you want to stop listening for requests you can do this by calling
\code{.cpp}
auto *context = us::GetModuleContext();
auto managerRef = context->GetServiceReference<IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
managerService->HandleDeleteObserver(this, uri);
}
}
\endcode
You don't have to specify a uri in the HandleDeleteObserver method, if you only call <code>managerService->HandleDeleteObserver(this);</code>, all uris you receive requests for are deleted and you aren't listening to any requests anymore.
\subsection Client_Use Use from a Client perspective
The following example shows how to send requests from a client perspective:
\code{.cpp}
//Get the microservice
auto *context = us::ModuleRegistry::GetModule(1)->GetModuleContext();
auto managerRef = context->GetServiceReference<mitk::IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
//Call the send request method which starts the actual request
managerService
->SendRequest(U("https://jsonplaceholder.typicode.com/posts/1"))
.then([=](pplx::task<web::json::value> resultTask)/*It is important to use task-based continuation*/ {
try
{
//Get the result of the request
//This will throw an exception if the ascendent task threw an exception (e.g. invalid URI)
web::json::value result = resultTask.get();
//Do something with the result (e.g. convert it to a QString to update an UI element)
utility::string_t stringT = result.to_string();
std::string stringStd(stringT.begin(), stringT.end());
QString stringQ = QString::fromStdString(stringStd);
//Note: if you want to update your UI, do this by using signals and slots.
//The UI can't be updated from a Thread different to the Qt main thread
emit UpdateLabel(stringQ);
}
catch (const mitk::Exception &exception)
{
//Exceptions from ascendent tasks are catched here
MITK_ERROR << exception.what();
return;
}
});
}
}
\endcode
The steps you need to make are the following:
<ol>
<li> Get the microservice. You can get the microservice via the module context. If you want to use the microservice within a plug-in, you need to get the module context from the <code>us::ModuleRegistry</code>.
<li> Call the <code>SendRequest</code> method. This will start the request itself and is performed asynchronously. As soon as the response is sent by the server, the <code>.then(...)</code> block is executed.
<li> Choose parameters for <code>.then(...)</code> block. For exception handling, it is important to choose <code>pplx::task<web::json::value> </code>. This is a task-based continuation.
For more information, visit https://docs.microsoft.com/en-us/cpp/parallel/concrt/exception-handling-in-the-concurrency-runtime?view=vs-2017.
<li> Get the result of the request. You can get the JSON-value of the result by callint <code>.get()</code>. At this point, an exception is thrown if something in the previous tasks threw an exception.
<li> Do something with the result.
\note If you want to modify GUI elements within the <code>.then(...)</code> block, you need to do this by using signals and slots because GUI elements can only be modified by th Qt Main Thread.
For more information, visit https://doc.qt.io/qt-6/thread-basics.html#gui-thread-and-worker-thread
<li> Exception handling. Here you can define the behaviour if an exception is thrown, exceptions from ascendent tasks are also catched here.
</ol>
Code, which is followed by this codeblock shown above will be performed asynchronously while waiting for the result.
Besides Get-Requests, you can also perform Put or Post requests by specifying a <code>RequestType</code> in the <code>SendRequest</code> method.
The following example shows, how you can perform multiple tasks, encapsulated to one joined task. The steps are based on the example for one request and only the specific steps for encapsulation are described.
\code{.cpp}
//Get the microservice
//Get microservice
auto *context = us::ModuleRegistry::GetModule(1)->GetModuleContext();
auto managerRef = context->GetServiceReference<mitk::IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
//Create multiple tasks e.g. as shown below
std::vector<pplx::task<void>> tasks;
for (int i = 0; i < 20; i++)
{
pplx::task<void> singleTask = managerService->SendRequest(L"https://jsonplaceholder.typicode.com/posts/1")
.then([=](pplx::task<web::json::value> resultTask) {
//Do something when a single task is done
try
{
resultTask.get();
emit UpdateProgressBar();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
tasks.emplace_back(singleTask);
}
//Create a joinTask which includes all tasks you've created
auto joinTask = pplx::when_all(begin(tasks), end(tasks));
//Run asynchonously
joinTask.then([=](pplx::task<void> resultTask) {
//Do something when all tasks are finished
try
{
resultTask.get();
emit UpdateLabel("All tasks finished");
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
}
\endcode
The steps you need to make are the following:
<ol>
<li> Get the microservice. See example above.
<li> Create multiple tasks. In this example, 20 identical tasks are created and are saved into a vector. In general, it is possible to place any tasks in that vector.
<li> Do something when a single task is done. Here, an action is performed if a single tasks is finished. In this example, a progress bar is loaded by a specific number of percent.
<li> Create a joinTask. Here, all small tasks are encapsulated in one big task.
<li> Run joinTask asynchonously. The <code>then(...)</code> of the joinTask is performed when all single tasks are finished.
<li> Do something when all tasks are finished. The handling of the end of a joinTask is equivalent to the end of a single tasks.
</ol>
*/
diff --git a/Modules/REST/include/mitkIRESTManager.h b/Modules/REST/include/mitkIRESTManager.h
index a40a9432fb..609779776e 100644
--- a/Modules/REST/include/mitkIRESTManager.h
+++ b/Modules/REST/include/mitkIRESTManager.h
@@ -1,133 +1,133 @@
/*============================================================================
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 mitkIRESTManager_h
#define mitkIRESTManager_h
#include <mitkServiceInterface.h>
#include <MitkRESTExports.h>
#include <mitkRESTUtil.h>
#include <cpprest/json.h>
#include <cpprest/uri.h>
#include <cpprest/http_client.h>
namespace mitk
{
class IRESTObserver;
class RESTServer;
/**
* @class IRESTManager
* @brief This is a microservice interface for managing REST requests.
*/
class MITKREST_EXPORT IRESTManager
{
public:
virtual ~IRESTManager();
/**
* @brief request type for client requests by calling SendRequest
*/
enum class RequestType
{
Get,
Post,
Put
};
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
virtual pplx::task<web::json::value> SendRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::map<utility::string_t, utility::string_t> headers = {}) = 0;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @param filePath the file path to store the request to (optional)
* @return task to wait for
*/
virtual pplx::task<web::json::value> SendJSONRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const web::json::value *body = nullptr,
const std::map<utility::string_t, utility::string_t> headers = {},
const utility::string_t &filePath = {}
) = 0;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
virtual pplx::task<web::json::value> SendBinaryRequest(const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::vector<unsigned char> *body = {},
const std::map<utility::string_t, utility::string_t> headers = {}) = 0;
/**
* @brief starts listening for requests if there isn't another observer listening and the port is free
*
* @param uri defines the URI for which incoming requests should be send to the observer
* @param observer the observer which handles the incoming requests
*/
virtual void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) = 0;
/**
* @brief Handles incoming requests by notifying the observer which should receive it
*
* @param uri defines the URI of the request
* @param body the body of the request
* @param method the http method of the request
* @param headers the http headers of the request
* @return the response
*/
virtual web::http::http_response Handle(const web::uri &uri,
const web::json::value &body,
const web::http::method &method,
const mitk::RESTUtil::ParamMap &headers) = 0;
/**
* @brief Handles the deletion of an observer for all or a specific uri
*
* @param observer the observer which shouldn't receive requests anymore
* @param uri the uri for which the observer doesn't handle requests anymore (optional)
*/
virtual void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = {}) = 0;
virtual const std::map<int, RESTServer *>& GetServerMap() = 0;
virtual const std::map<std::pair<int, utility::string_t>, IRESTObserver *>& GetObservers() = 0;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::IRESTManager, "org.mitk.IRESTManager")
#endif
diff --git a/Modules/REST/test/mitkRESTClientTest.cpp b/Modules/REST/test/mitkRESTClientTest.cpp
index cad9e820a8..7acdbc44b3 100644
--- a/Modules/REST/test/mitkRESTClientTest.cpp
+++ b/Modules/REST/test/mitkRESTClientTest.cpp
@@ -1,264 +1,264 @@
/*============================================================================
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 <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkIRESTManager.h>
#include <mitkIRESTObserver.h>
#include <mitkRESTClient.h>
#include <mitkRESTUtil.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include <vtkDebugLeaks.h>
#include <atomic>
class mitkRESTClientTestSuite : public mitk::TestFixture, mitk::IRESTObserver
{
CPPUNIT_TEST_SUITE(mitkRESTClientTestSuite);
// MITK_TEST(GetRequestValidURI_ReturnsExpectedJSON); GET requests do not support content yet?
MITK_TEST(MultipleGetRequestValidURI_AllTasksFinish);
// MITK_TEST(PutRequestValidURI_ReturnsExpectedJSON); Does not work reliably on dart clients
// MITK_TEST(PostRequestValidURI_ReturnsExpectedJSON); -- " --
MITK_TEST(GetRequestInvalidURI_ThrowsException);
MITK_TEST(PutRequestInvalidURI_ThrowsException);
MITK_TEST(PostRequestInvalidURI_ThrowsException);
CPPUNIT_TEST_SUITE_END();
public:
mitk::IRESTManager *m_Service;
web::json::value m_Data;
web::http::http_response Notify(const web::uri &,
const web::json::value &,
const web::http::method &,
const mitk::RESTUtil::ParamMap &) override
{
auto response = web::http::http_response();
response.set_body(m_Data);
response.set_status_code(web::http::status_codes::OK);
return response;
}
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_Data = web::json::value();
m_Data[U("userId")] = web::json::value(1);
m_Data[U("id")] = web::json::value(1);
m_Data[U("title")] = web::json::value(U("this is a title"));
m_Data[U("body")] = web::json::value(U("this is a body"));
us::ServiceReference<mitk::IRESTManager> serviceRef =
us::GetModuleContext()->GetServiceReference<mitk::IRESTManager>();
if (serviceRef)
{
m_Service = us::GetModuleContext()->GetService(serviceRef);
}
if (!m_Service)
{
CPPUNIT_FAIL("Getting Service in setUp() failed");
}
m_Service->ReceiveRequest(U("http://localhost:8080/clienttest"), this);
}
void tearDown() override { m_Service->HandleDeleteObserver(this); }
void GetRequestValidURI_ReturnsExpectedJSON()
{
web::json::value result;
m_Service->SendRequest(U("http://localhost:8080/clienttest"))
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", result == m_Data);
}
void MultipleGetRequestValidURI_AllTasksFinish()
{
std::atomic<int> count {0};
// Create multiple tasks e.g. as shown below
std::vector<pplx::task<void>> tasks;
for (int i = 0; i < 20; ++i)
{
pplx::task<void> singleTask = m_Service->SendRequest(U("http://localhost:8080/clienttest"))
.then([&](pplx::task<web::json::value> resultTask) {
// Do something when a single task is done
try
{
resultTask.get();
count += 1;
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
tasks.emplace_back(singleTask);
}
// Create a joinTask which includes all tasks you've created
auto joinTask = pplx::when_all(begin(tasks), end(tasks));
// Run asynchonously
joinTask
.then([&](pplx::task<void> resultTask) {
// Do something when all tasks are finished
try
{
resultTask.get();
count += 1;
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE("Multiple Requests", 21 == count);
}
void PutRequestValidURI_ReturnsExpectedJSON()
{
// optional: link might get invalid or content is changed
web::json::value result;
m_Service
->SendJSONRequest(
U("https://jsonplaceholder.typicode.com/posts/1"), mitk::IRESTManager::RequestType::Put)
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE(
"Result is the expected JSON value, check if the link is still valid since this is an optional test",
result == m_Data);
}
void PostRequestValidURI_ReturnsExpectedJSON()
{
// optional: link might get invalid or content is changed
web::json::value result;
web::json::value data;
data[U("userId")] = m_Data[U("userId")];
data[U("title")] = m_Data[U("title")];
data[U("body")] = m_Data[U("body")];
m_Service
->SendJSONRequest(U("https://jsonplaceholder.typicode.com/posts"), mitk::IRESTManager::RequestType::Post, &data)
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
data[U("id")] = web::json::value(101);
CPPUNIT_ASSERT_MESSAGE(
"Result is the expected JSON value, check if the link is still valid since this is an optional test",
result == data);
}
void PostRequestHeaders_Success()
{
mitk::RESTUtil::ParamMap headers;
headers.insert(mitk::RESTUtil::ParamMap::value_type(
U("Content-Type"), U("multipart/related; type=\"application/dicom\"; boundary=boundary")));
m_Service->SendRequest(U("http://localhost:8080/clienttest")).then([&](pplx::task<web::json::value> resultTask) {
// Do something when a single task is done
try
{
resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
}
void GetException()
{
// Method which makes a get request to an invalid uri
web::json::value result;
m_Service->SendRequest(U("http://localhost:1234/invalid"))
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void GetRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(GetException(), mitk::Exception); }
void PutException()
{
// Method which makes a put request to an invalid uri
web::json::value result;
m_Service->SendJSONRequest(U("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Put, &m_Data)
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void PutRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PutException(), mitk::Exception); }
void PostException()
{
// Method which makes a post request to an invalid uri
web::json::value result;
m_Service->SendJSONRequest(U("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Post, &m_Data)
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void PostRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PostException(), mitk::Exception); }
};
MITK_TEST_SUITE_REGISTRATION(mitkRESTClient)
diff --git a/Modules/REST/test/mitkRESTServerTest.cpp b/Modules/REST/test/mitkRESTServerTest.cpp
index 1292d98b5f..22f1fe65cf 100644
--- a/Modules/REST/test/mitkRESTServerTest.cpp
+++ b/Modules/REST/test/mitkRESTServerTest.cpp
@@ -1,252 +1,252 @@
/*============================================================================
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.
============================================================================*/
#ifdef _WIN32
#include <Windows.h>
#endif
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkIRESTManager.h>
#include <mitkIRESTObserver.h>
#include <mitkRESTServer.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include <vtkDebugLeaks.h>
class mitkRESTServerTestSuite : public mitk::TestFixture, mitk::IRESTObserver
{
CPPUNIT_TEST_SUITE(mitkRESTServerTestSuite);
MITK_TEST(OpenListener_Succeed);
MITK_TEST(TwoListenerSameHostSamePort_OnlyOneOpened);
MITK_TEST(CloseListener_Succeed);
MITK_TEST(OpenMultipleListenerCloseOne_Succeed);
MITK_TEST(OpenMultipleListenerCloseAll_Succeed);
// MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON); GET requests do not support content yet?
MITK_TEST(CloseListener_NoRequestPossible);
MITK_TEST(OpenListenerGetRequestDifferentPath_ReturnNotFound);
MITK_TEST(OpenListenerCloseAndReopen_Succeed);
MITK_TEST(HandleHeader_Succeed);
CPPUNIT_TEST_SUITE_END();
public:
mitk::IRESTManager *m_Service;
web::json::value m_Data;
web::http::http_response Notify(const web::uri &,
const web::json::value &,
const web::http::method &,
const mitk::RESTUtil::ParamMap &headers) override
{
auto response = web::http::http_response();
response.set_body(m_Data);
mitk::RESTUtil::ParamMap::const_iterator contentTypePos = headers.find(U("Content-Type"));
if (contentTypePos != headers.end() && contentTypePos->second == U("awesome/type"))
{
m_Data[U("result")] = web::json::value(U("awesome/type"));
}
return response;
}
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_Data = web::json::value();
m_Data[U("userId")] = web::json::value(1);
m_Data[U("id")] = web::json::value(1);
m_Data[U("title")] = web::json::value(U("this is a title"));
m_Data[U("body")] = web::json::value(U("this is a body"));
auto serviceRef = us::GetModuleContext()->GetServiceReference<mitk::IRESTManager>();
if (serviceRef)
m_Service = us::GetModuleContext()->GetService(serviceRef);
if (!m_Service)
CPPUNIT_FAIL("Getting Service in setUp() failed");
}
void tearDown() override { m_Service->HandleDeleteObserver(this); }
void OpenListener_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
}
void TwoListenerSameHostSamePort_OnlyOneOpened()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
m_Service->ReceiveRequest(U("http://localhost:8080/serverexample"), this);
CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, observer map size is two",
2 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, server map size is one",
1 == m_Service->GetServerMap().size());
}
void CloseListener_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size());
}
void OpenMultipleListenerCloseOne_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
m_Service->ReceiveRequest(U("http://localhost:8090/serverexample"), this);
CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this, U("http://localhost:8080/servertest"));
CPPUNIT_ASSERT_MESSAGE("Closed one of two listeners, observer map is size is one",
1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed one of two listener, server map size is one", 1 == m_Service->GetServerMap().size());
}
void OpenMultipleListenerCloseAll_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
m_Service->ReceiveRequest(U("http://localhost:8090/serverexample"), this);
CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed all listeners, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed all listeners, server map is empty", 0 == m_Service->GetServerMap().size());
}
void OpenListenerGetRequestSamePath_ReturnExpectedJSON()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
web::json::value result;
auto body = web::json::value();
m_Service->SendRequest(U("http://localhost:8080/servertest"))
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE("Opened listener and send request to same uri, returned expected JSON", result == m_Data);
}
void RequestToClosedListener()
{
web::json::value result;
m_Service->SendRequest(U("http://localhost:8080/servertest"))
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void CloseListener_NoRequestPossible()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size());
CPPUNIT_ASSERT_THROW(RequestToClosedListener(), mitk::Exception);
}
void RequestToDifferentPathNotFound()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
web::json::value result;
m_Service->SendRequest(U("http://localhost:8080/serverexample"))
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void OpenListenerGetRequestDifferentPath_ReturnNotFound()
{
CPPUNIT_ASSERT_THROW(RequestToDifferentPathNotFound(), mitk::Exception);
}
void OpenListenerCloseAndReopen_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size());
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Reopened listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Reopened listener, server map size is one", 1 == m_Service->GetServerMap().size());
}
void HandleHeader_Succeed()
{
mitk::RESTUtil::ParamMap headers;
headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Content-Type"), U("awesome/type")));
m_Service->SendRequest(U("http://localhost:8080/clienttest")).then([&](pplx::task<web::json::value> resultTask) {
// Do something when a single task is done
try
{
auto result = resultTask.get();
- CPPUNIT_ASSERT_MESSAGE("Sent Header is not successfull transfered to server",
+ CPPUNIT_ASSERT_MESSAGE("Sent Header is not successful transferred to server",
result[U("result")].as_string() == U("awesome/type"));
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
}
};
MITK_TEST_SUITE_REGISTRATION(mitkRESTServer)
diff --git a/Modules/RESTService/include/mitkRESTManager.h b/Modules/RESTService/include/mitkRESTManager.h
index 5ea1b5e457..2e62c12ea7 100644
--- a/Modules/RESTService/include/mitkRESTManager.h
+++ b/Modules/RESTService/include/mitkRESTManager.h
@@ -1,153 +1,153 @@
/*============================================================================
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 mitkRESTManager_h
#define mitkRESTManager_h
#include <MitkRESTServiceExports.h>
#include <mitkIRESTManager.h>
#include <mitkRESTUtil.h>
namespace mitk
{
/**
* @class RESTManager
* @brief this is a microservice for managing REST-requests, used for non-qt applications.
*
* RESTManagerQt in the CppRestSdkQt module inherits from this class and is the equivalent microservice
* used for Qt applications.
*/
class MITKRESTSERVICE_EXPORT RESTManager : public IRESTManager
{
public:
RESTManager();
~RESTManager() override;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
pplx::task<web::json::value> SendRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::map<utility::string_t, utility::string_t> headers = {}) override;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @param filePath the file path to store the request to (optional)
* @return task to wait for
*/
pplx::task<web::json::value> SendJSONRequest(const web::uri &uri,
const RequestType &type = RequestType::Get,
const web::json::value *body = nullptr,
const std::map<utility::string_t, utility::string_t> headers = {},
const utility::string_t &filePath = {}) override;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
pplx::task<web::json::value> SendBinaryRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::vector<unsigned char> *body = {},
const std::map<utility::string_t, utility::string_t> headers = {}) override;
/**
* @brief starts listening for requests if there isn't another observer listening and the port is free
*
* @param uri defines the URI for which incoming requests should be send to the observer
* @param observer the observer which handles the incoming requests
*/
void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) override;
/**
* @brief Handles incoming requests by notifying the observer which should receive it
*
* @param uri defines the URI of the request
* @param body the body of the request
* @param method the http method of the request
* @param headers the http headers of the request
* @return the response
*/
web::http::http_response Handle(const web::uri &uri,
const web::json::value &body,
const web::http::method &method,
const mitk::RESTUtil::ParamMap &headers) override;
/**
* @brief Handles the deletion of an observer for all or a specific uri
*
* @param observer the observer which shouldn't receive requests anymore
* @param uri the uri for which the observer doesn't handle requests anymore (optional)
*/
void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = {}) override;
/**
* @brief internal use only
*/
const std::map<int, RESTServer *> &GetServerMap() override;
std::map<std::pair<int, utility::string_t>, IRESTObserver *> &GetObservers() override;
private:
/**
* @brief adds an observer if a port is free, called by ReceiveRequest method
*
* @param uri the uri which builds the key for the observer map
* @param observer the observer which is added
*/
void AddObserver(const web::uri &uri, IRESTObserver *observer);
/**
* @brief handles server management if there is already a server under a port, called by ReceiveRequest method
*
* @param uri the uri which which is requested to be added
* @param observer the observer which proceeds the request
*/
void RequestForATakenPort(const web::uri &uri, IRESTObserver *observer);
/**
* @brief deletes an observer, called by HandleDeleteObserver method
*
* @param it the iterator comparing the observers in HandleDeleteObserver method
* @return bool if there is another observer under the port
*/
bool DeleteObserver(std::map<std::pair<int, utility::string_t>, IRESTObserver *>::iterator &it);
void SetServerMap(const int port, RESTServer *server);
void DeleteFromServerMap(const int port);
void SetObservers(const std::pair<int, utility::string_t> key, IRESTObserver *observer);
std::map<int, RESTServer *> m_ServerMap; // Map with port server pairs
std::map<std::pair<int, utility::string_t>, IRESTObserver *> m_Observers; // Map with all observers
};
} // namespace mitk
#endif
diff --git a/Modules/RESTService/src/mitkRESTManager.cpp b/Modules/RESTService/src/mitkRESTManager.cpp
index 1f59480cd6..d619170369 100644
--- a/Modules/RESTService/src/mitkRESTManager.cpp
+++ b/Modules/RESTService/src/mitkRESTManager.cpp
@@ -1,258 +1,258 @@
/*============================================================================
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 <mitkIRESTObserver.h>
#include <mitkRESTClient.h>
#include <mitkRESTManager.h>
#include <mitkRESTServer.h>
#include <mitkExceptionMacro.h>
#include <mitkLog.h>
mitk::RESTManager::RESTManager() {}
mitk::RESTManager::~RESTManager() {}
pplx::task<web::json::value> mitk::RESTManager::SendRequest(
const web::uri &uri, const RequestType &type, const std::map<utility::string_t, utility::string_t> headers)
{
pplx::task<web::json::value> answer;
auto client = new RESTClient;
switch (type)
{
case RequestType::Get:
answer = client->Get(uri, headers);
break;
default:
mitkThrow() << "Request Type not supported";
}
return answer;
}
pplx::task<web::json::value> mitk::RESTManager::SendBinaryRequest(
const web::uri &uri,
const RequestType &type,
const std::vector<unsigned char> *content,
const std::map<utility::string_t, utility::string_t> headers)
{
pplx::task<web::json::value> answer;
auto client = new RESTClient;
switch (type)
{
case RequestType::Post:
if (nullptr == content)
MITK_WARN << "Content for post is empty, this will create an empty resource";
answer = client->Post(uri, content, headers);
break;
default:
mitkThrow() << "Request Type not supported for binary data";
}
return answer;
}
pplx::task<web::json::value> mitk::RESTManager::SendJSONRequest(
const web::uri &uri,
const RequestType &type,
const web::json::value *content,
const std::map<utility::string_t, utility::string_t> headers,
const utility::string_t &filePath)
{
pplx::task<web::json::value> answer;
auto client = new RESTClient;
switch (type)
{
case RequestType::Get:
answer = !filePath.empty() ? client->Get(uri, filePath, headers) : client->Get(uri, headers);
break;
case RequestType::Post:
if (nullptr == content)
MITK_WARN << "Content for post is empty, this will create an empty resource";
answer = client->Post(uri, content, headers);
break;
case RequestType::Put:
if (nullptr == content)
- MITK_WARN << "Content for put is empty, this will empty the ressource";
+ MITK_WARN << "Content for put is empty, this will empty the resource";
answer = client->Put(uri, content);
break;
default:
mitkThrow() << "Request Type not supported";
}
return answer;
}
void mitk::RESTManager::ReceiveRequest(const web::uri &uri, mitk::IRESTObserver *observer)
{
// New instance of RESTServer in m_ServerMap, key is port of the request
auto port = uri.port();
// Checking if port is free to add a new Server
if (0 == m_ServerMap.count(port))
{
this->AddObserver(uri, observer);
// creating server instance
auto server = new RESTServer(uri.authority());
// add reference to server instance to map
m_ServerMap[port] = server;
// start Server
server->OpenListener();
}
// If there is already a server under this port
else
{
this->RequestForATakenPort(uri, observer);
}
}
web::http::http_response mitk::RESTManager::Handle(const web::uri &uri,
const web::json::value &body,
const web::http::method &method,
const mitk::RESTUtil::ParamMap &headers)
{
// Checking if there is an observer for the port and path
auto key = std::make_pair(uri.port(), uri.path());
if (0 != m_Observers.count(key))
{
return m_Observers[key]->Notify(uri, body, method, headers);
}
// No observer under this port, return null which results in status code 404 (s. RESTServer)
else
{
MITK_WARN << "No Observer can handle the data";
web::http::http_response response(web::http::status_codes::BadGateway);
response.set_body(U("No one can handle the request under the given port."));
return response;
}
}
void mitk::RESTManager::HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri)
{
for (auto it = m_Observers.begin(); it != m_Observers.end();)
{
mitk::IRESTObserver *obsMap = it->second;
- // Check wether observer is at this place in map
+ // Check whether observer is at this place in map
if (observer == obsMap)
{
- // Check wether it is the right uri to be deleted
+ // Check whether it is the right uri to be deleted
if (uri.is_empty() || uri.path() == it->first.second)
{
int port = it->first.first;
bool noObserverForPort = this->DeleteObserver(it);
if (noObserverForPort)
{
// there isn't an observer at this port, delete m_ServerMap entry for this port
// close listener
m_ServerMap[port]->CloseListener();
delete m_ServerMap[port];
// delete server from map
m_ServerMap.erase(port);
}
}
else
{
++it;
}
}
else
{
++it;
}
}
}
const std::map<int, mitk::RESTServer *> &mitk::RESTManager::GetServerMap()
{
return m_ServerMap;
}
std::map<std::pair<int, utility::string_t>, mitk::IRESTObserver *> &mitk::RESTManager::GetObservers()
{
return m_Observers;
}
void mitk::RESTManager::AddObserver(const web::uri &uri, IRESTObserver *observer)
{
// new observer has to be added
std::pair<int, utility::string_t> key(uri.port(), uri.path());
m_Observers[key] = observer;
}
void mitk::RESTManager::RequestForATakenPort(const web::uri &uri, IRESTObserver *observer)
{
// Same host, means new observer but not a new server instance
if (uri.authority() == m_ServerMap[uri.port()]->GetUri())
{
// new observer has to be added
std::pair<int, utility::string_t> key(uri.port(), uri.path());
// only add a new observer if there isn't already an observer for this uri
if (0 == m_Observers.count(key))
{
this->AddObserver(uri, observer);
}
else
{
- MITK_ERROR << "Threre is already a observer handeling this data";
+ MITK_ERROR << "There is already an observer handling this data";
}
}
// Error, since another server can't be added under this port
else
{
MITK_ERROR << "There is already another server listening under this port";
}
}
bool mitk::RESTManager::DeleteObserver(std::map<std::pair<int, utility::string_t>, IRESTObserver *>::iterator &it)
{
int port = it->first.first;
it = m_Observers.erase(it);
for (auto observer : m_Observers)
{
if (port == observer.first.first)
{
// there still exists an observer for this port
return false;
}
}
return true;
}
void mitk::RESTManager::SetServerMap(const int port, RESTServer *server)
{
m_ServerMap[port] = server;
}
void mitk::RESTManager::DeleteFromServerMap(const int port)
{
m_ServerMap.erase(port);
}
void mitk::RESTManager::SetObservers(const std::pair<int, utility::string_t> key, IRESTObserver *observer)
{
m_Observers[key] = observer;
}
diff --git a/Modules/RT/include/mitkDoseImageVtkMapper2D.h b/Modules/RT/include/mitkDoseImageVtkMapper2D.h
index e659d17103..d87eb8812a 100644
--- a/Modules/RT/include/mitkDoseImageVtkMapper2D.h
+++ b/Modules/RT/include/mitkDoseImageVtkMapper2D.h
@@ -1,303 +1,303 @@
/*============================================================================
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 mitkDoseImageVtkMapper2D_h
#define mitkDoseImageVtkMapper2D_h
//MITK
#include <mitkCommon.h>
#include <MitkRTExports.h>
//MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkVtkMapper.h"
#include "mitkExtractSliceFilter.h"
//VTK
#include <vtkSmartPointer.h>
#include <vtkPropAssembly.h>
#include <vtkCellArray.h>
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageExtractComponents;
class vtkImageReslice;
class vtkImageChangeInformation;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkMitkApplyLevelWindowToRGBFilter;
class vtkMitkLevelWindowFilter;
namespace mitk {
/** \brief Mapper to resample and display 2D slices of a 3D image.
*
* First, the image is resliced by means of vtkImageReslice. The volume image
* serves as input to the mapper in addition to spatial placement of the slice and a few other
* properties such as thick slices. This code was already present in the old version
* (mitkImageMapperGL2D).
*
* Next, the obtained slice (m_ReslicedImage) is put into a vtkMitkLevelWindowFilter
* and the scalar levelwindow, opacity levelwindow and optional clipping to
* local image bounds are applied
*
* Next, the output of the vtkMitkLevelWindowFilter is used to create a texture
* (m_Texture) and a plane onto which the texture is rendered (m_Plane). For
* mapping purposes, a vtkPolyDataMapper (m_Mapper) is utilized. Orthographic
* projection is applied to create the effect of a 2D image. The mapper and the
* texture are assigned to the actor (m_Actor) which is passed to the VTK rendering
* pipeline via the method GetVtkProp().
*
* In order to transform the textured plane to the correct position in space, the
* same transformation as used for reslicing is applied to both the camera and the
* vtkActor. All important steps are explained in more detail below. The resulting
* 2D image (by reslicing the underlying 3D input image appropriately) can either
* be directly rendered in a 2D view or just be calculated to be used later by another
* rendering entity, e.g. in texture mapping in a 3D view.
*
* Properties that can be set for images and influence the imageMapper2D are:
*
* - \b "opacity": (FloatProperty) Opacity of the image
* - \b "color": (ColorProperty) Color of the image
* - \b "LookupTable": (mitkLookupTableProperty) If this property is set,
* the default lookuptable will be ignored and the "LookupTable" value
* will be used instead.
* - \b "Image Rendering.Mode": This property decides which mode is used to render images. (E.g. if a lookup table or a transferfunction is applied). Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
* - \b "Image Rendering.Transfer Function": (mitkTransferFunctionProperty) If this
* property is set, a color transferfunction will be used to color the image.
* - \b "binary": (BoolProperty) is the image a binary image or not
* - \b "outline binary": (BoolProperty) show outline of the image or not
* - \b "texture interpolation": (BoolProperty) texture interpolation of the image
* - \b "reslice interpolation": (VtkResliceInterpolationProperty) reslice interpolation of the image
* - \b "in plane resample extent by geometry": (BoolProperty) Do it or not
* - \b "bounding box": (BoolProperty) Is the Bounding Box of the image shown or not
* - \b "layer": (IntProperty) Layer of the image
* - \b "volume annotation color": (ColorProperty) color of the volume annotation, TODO has to be reimplemented
* - \b "volume annotation unit": (StringProperty) annotation unit as string (does not implicit convert the unit!)
unit is ml or cm3, TODO has to be reimplemented
* The default properties are:
* - \b "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite )
* - \b "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite )
* - \b "binary", mitk::BoolProperty::New( true ), renderer, overwrite )
* - \b "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite )
* - \b "texture interpolation", mitk::BoolProperty::New( false ) )
* - \b "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() )
* - \b "in plane resample extent by geometry", mitk::BoolProperty::New( false ) )
* - \b "bounding box", mitk::BoolProperty::New( false ) )
* - \b "layer", mitk::IntProperty::New(10), renderer, overwrite)
* - \b "Image Rendering.Transfer Function": Default color transfer function for CTs
* - \b "LookupTable": Rainbow color.
* If the modality-property is set for an image, the mapper uses modality-specific default properties,
* e.g. color maps, if they are defined.
* \ingroup Mapper
*/
class MITKRT_EXPORT DoseImageVtkMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro( DoseImageVtkMapper2D,VtkMapper );
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Get the Image to map */
const mitk::Image *GetInput(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer * renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKRT_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
/** \brief Actor of a 2D render window. */
vtkSmartPointer<vtkActor> m_Actor;
vtkSmartPointer<vtkPropAssembly> m_Actors;
/** \brief Mapper of a 2D render window. */
vtkSmartPointer<vtkPolyDataMapper> m_Mapper;
vtkSmartPointer<vtkImageExtractComponents> m_VectorComponentExtractor;
/** \brief Current slice of a 2D render window.*/
vtkSmartPointer<vtkImageData> m_ReslicedImage;
/** \brief Empty vtkPolyData that is set when rendering geometry does not
* intersect the image geometry.
* \warning This member variable is set to nullptr,
* if no image geometry is inside the plane geometry
* of the respective render window. Any user of this
* slice has to check whether it is set to nullptr!
*/
vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
/** \brief Plane on which the slice is rendered as texture. */
vtkSmartPointer<vtkPlaneSource> m_Plane;
/** \brief The texture which is used to render the current slice. */
vtkSmartPointer<vtkTexture> m_Texture;
/** \brief The lookuptables for colors and level window */
vtkSmartPointer<vtkLookupTable> m_DefaultLookupTable;
vtkSmartPointer<vtkLookupTable> m_BinaryLookupTable;
vtkSmartPointer<vtkLookupTable> m_ColorLookupTable;
/** \brief The actual reslicer (one per renderer) */
mitk::ExtractSliceFilter::Pointer m_Reslicer;
/** \brief Filter for thick slices */
vtkSmartPointer<vtkMitkThickSlicesFilter> m_TSFilter;
- /** \brief PolyData object containg all lines/points needed for outlining the contour.
+ /** \brief PolyData object containing all lines/points needed for outlining the contour.
This container is used to save a computed contour for the next rendering execution.
For instance, if you zoom or pann, there is no need to recompute the contour. */
vtkSmartPointer<vtkPolyData> m_OutlinePolyData;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType* m_mmPerPixel;
/** \brief This filter is used to apply the level window to Grayvalue and RBG(A) images. */
vtkSmartPointer<vtkMitkLevelWindowFilter> m_LevelWindowFilter;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default deconstructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage* GetLocalStorage(mitk::BaseRenderer* renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = nullptr, bool overwrite = false);
/** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function).
* Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
*/
void ApplyRenderingMode(mitk::BaseRenderer *renderer);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer* renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]);
/** \brief Generates a vtkPolyData object containing the outline of a given binary slice.
\param renderer: Pointer to the renderer containing the needed information
\note This code is based on code from the iil library.
*/
vtkSmartPointer<vtkPolyData> CreateOutlinePolyData(mitk::BaseRenderer* renderer);
/** Default constructor */
DoseImageVtkMapper2D();
/** Default deconstructor */
~DoseImageVtkMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
/** \brief This method uses the vtkCamera clipping range and the layer property
- * to calcualte the depth of the object (e.g. image or contour). The depth is used
+ * to calculate the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer* renderer);
/** \brief This method applies (or modifies) the lookuptable for all types of images.
* \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode'
* which uses the lookup table must be set.
*/
void ApplyLookuptable(mitk::BaseRenderer* renderer);
/** \brief This method applies a color transfer function.
- * Internally, a vtkColorTransferFunction is used. This is usefull for coloring continous
+ * Internally, a vtkColorTransferFunction is used. This is useful for coloring continuous
* images (e.g. float)
* \warning To use the color transfer function, the property 'Image Rendering.Transfer Function' must be set and a 'Image Rendering.Mode' which uses the color transfer function must be set.
*/
void ApplyColorTransferFunction(mitk::BaseRenderer* renderer);
/**
* @brief ApplyLevelWindow Apply the level window for the given renderer.
* \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses the level window must be set.
* @param renderer Level window for which renderer?
*/
void ApplyLevelWindow(mitk::BaseRenderer* renderer);
/** \brief Set the color of the image/polydata */
void ApplyColor( mitk::BaseRenderer* renderer );
/** \brief Set the opacity of the actor. */
void ApplyOpacity( mitk::BaseRenderer* renderer );
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given PlaneGeometry intersects the given
* SlicedGeometry3D. It calculates the distance of the PlaneGeometry to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
**/
bool RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry );
private:
void CreateLevelOutline(mitk::BaseRenderer* renderer, const mitk::IsoDoseLevel* level, float pref, vtkSmartPointer<vtkPoints> points, vtkSmartPointer<vtkCellArray> lines, vtkSmartPointer<vtkUnsignedCharArray> colors);
};
} // namespace mitk
#endif
diff --git a/Modules/RT/include/mitkRTConstants.h b/Modules/RT/include/mitkRTConstants.h
index 5efef8090c..bba72c91a8 100644
--- a/Modules/RT/include/mitkRTConstants.h
+++ b/Modules/RT/include/mitkRTConstants.h
@@ -1,107 +1,107 @@
/*============================================================================
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 mitkRTConstants_h
#define mitkRTConstants_h
#include <string>
#include "MitkRTExports.h"
namespace mitk
{
struct MITKRT_EXPORT RTConstants
{
/**
* Name of the property that indicates if a data/node is a dose.
*/
static const std::string DOSE_PROPERTY_NAME;
/**
* Name of the property that encodes the prescribed dose associated with the data node
* If a RTPLAN file exists the value can be extracted from the tag (300A,0026) - Target Prescription Dose in the plan file.
*/
static const std::string PRESCRIBED_DOSE_PROPERTY_NAME;
/**
- * Name of the property that encodes the reference dose that should be used for relative dose vizualization/evaluation purpose.
- * It is often the prescribed dose but may differ e.g. when to dose distributions sould be compared using the same reference.
+ * Name of the property that encodes the reference dose that should be used for relative dose visualization/evaluation purpose.
+ * It is often the prescribed dose but may differ e.g. when to dose distributions should be compared using the same reference.
*/
static const std::string REFERENCE_DOSE_PROPERTY_NAME;
/**
* Name of the property that encodes the reference structure set.
*/
static const std::string REFERENCE_STRUCTURE_SET_PROPERTY_NAME;
/**
* Name of the property that encodes the optional string property holding the information from the tag (3004,0004) - Dose Type.
* This contains useful information for medical doctors
*/
static const std::string DOSE_TYPE_PROPERTY_NAME;
/**
* Name of the property that encodes the optional string property holding the description information from the tag (300A,0016) - Dose Reference Description.
*/
static const std::string REFERENCE_DESCRIPTION_DOSE_PROPERTY_NAME;
/**
* Name of the property that encodes the optional string property holding the information from the tag (3004,000A) - Dose Summation Type.
* This contains useful information for medical doctors
*/
static const std::string DOSE_SUMMATION_TYPE_PROPERTY_NAME;
/**
* Name of the property that encodes the number of fractions.
* It is for example in DICOM stored in tag (300A,0078) - Number of Fractions Prescribed (from the RTPLAN file if this file exists).
* This value could be used to further scale the dose according to dose summation type.
* For example a given plan consists of 8 fractions. Scaling the fraction dose by 8 gives the complete planned dose.
*/
static const std::string DOSE_FRACTION_COUNT_PROPERTY_NAME;
/**
* Name of the property that encodes the number of beams.
* It is for example in DICOM stored in tag (300A,0080) - Number of Beams (from the RTPLAN file if this file exists).
*/
static const std::string DOSE_FRACTION_NUMBER_OF_BEAMS_PROPERTY_NAME;
/**
* Name of the property that encodes the radiation type of beams.
* It is for example in DICOM stored in tag (300A,00C6) - Radiation Type (from the RTPLAN file if this file exists).
*/
static const std::string DOSE_RADIATION_TYPE_PROPERTY_NAME;
/**
* Name of the property that encodes if the iso line rendering should be activated for the node.
*/
static const std::string DOSE_SHOW_ISOLINES_PROPERTY_NAME;
/**
* Name of the property that encodes if the color wash rendering should be activated for the node.
*/
static const std::string DOSE_SHOW_COLORWASH_PROPERTY_NAME;
/**
* Name of the property that encodes if the set of iso levels should be used to visualize the dose distribution.
*/
static const std::string DOSE_ISO_LEVELS_PROPERTY_NAME;
/**
* Name of the property that encodes user defined iso values that mark special dose values in the distribution.
*/
static const std::string DOSE_FREE_ISO_VALUES_PROPERTY_NAME;
};
}
#endif
diff --git a/Modules/RT/src/mitkDICOMRTMimeTypes.cpp b/Modules/RT/src/mitkDICOMRTMimeTypes.cpp
index d72e25afa4..55f25884ea 100644
--- a/Modules/RT/src/mitkDICOMRTMimeTypes.cpp
+++ b/Modules/RT/src/mitkDICOMRTMimeTypes.cpp
@@ -1,244 +1,244 @@
/*============================================================================
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 <mitkDICOMRTMimeTypes.h>
#include <mitkIOMimeTypes.h>
#include <mitkDICOMDCMTKTagScanner.h>
#include <mitkDICOMTagPath.h>
#include <mitkDICOMFileReaderSelector.h>
#include <mitkDICOMFileReader.h>
#include <itksys/SystemTools.hxx>
namespace mitk
{
std::array<std::unique_ptr<CustomMimeType>, 3> DICOMRTMimeTypes::Get()
{
return {
std::make_unique<RTDoseMimeType>(),
std::make_unique<RTPlanMimeType>(),
std::make_unique<RTStructMimeType>()
};
}
// Mime Types
DICOMRTMimeTypes::RTDoseMimeType::RTDoseMimeType()
: CustomMimeType(DICOMRT_DOSE_MIMETYPE_NAME())
{
std::string category = "DICOMRT";
this->SetCategory(category);
this->SetComment("RTDose");
this->AddExtension("dcm");
}
bool DICOMRTMimeTypes::RTDoseMimeType::AppliesTo(const std::string &path) const
{
bool canRead( CustomMimeType::AppliesTo(path) );
if (!canRead)
{
return false;
}
// fix for bug 18572
// Currently this function is called for writing as well as reading, in that case
- // the image information can of course not be parsed or further identifyed.
- //so as a work arround we just return the current canRead if the file does not exist.
+ // the image information can of course not be parsed or further identified.
+ //so as a work around we just return the current canRead if the file does not exist.
if (!itksys::SystemTools::FileExists(path.c_str()))
{
return canRead;
}
// end fix for bug 18572
auto modality = GetModality(path);
if (modality == "RTDOSE")
{
return canReadByDicomFileReader(path);
}
return false;
}
std::string DICOMRTMimeTypes::GetModality(const std::string & path)
{
const auto modalityTagPath = DICOMTagPath(0x0008, 0x0060);
mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New();
scanner->SetInputFiles({ path });
scanner->AddTagPaths({ modalityTagPath });
scanner->Scan();
mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList();
std::string modality = "";
if (frames.empty())
return modality;
auto findings = frames.front()->GetTagValueAsString(modalityTagPath);
modality = findings.front().value;
return modality;
}
bool DICOMRTMimeTypes::canReadByDicomFileReader(const std::string & filename)
{
mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New();
selector->LoadBuiltIn3DConfigs();
selector->SetInputFiles({ filename });
mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages();
return reader.IsNotNull();
}
DICOMRTMimeTypes::RTDoseMimeType* DICOMRTMimeTypes::RTDoseMimeType::Clone() const
{
return new RTDoseMimeType(*this);
}
DICOMRTMimeTypes::RTStructMimeType::RTStructMimeType()
: CustomMimeType(DICOMRT_STRUCT_MIMETYPE_NAME())
{
std::string category = "DICOMRT";
this->SetCategory(category);
this->SetComment("RTStruct");
this->AddExtension("dcm");
}
bool DICOMRTMimeTypes::RTStructMimeType::AppliesTo(const std::string &path) const
{
bool canRead(CustomMimeType::AppliesTo(path));
if (!canRead) {
return false;
}
// fix for bug 18572
// Currently this function is called for writing as well as reading, in that case
- // the image information can of course not be parsed or further identifyed.
- //so as a work arround we just return the current canRead if the file does not exist.
+ // the image information can of course not be parsed or further identified.
+ //so as a work around we just return the current canRead if the file does not exist.
if (!itksys::SystemTools::FileExists(path.c_str()))
{
return canRead;
}
// end fix for bug 18572
auto modality = GetModality(path);
if (modality == "RTSTRUCT") {
return true;
}
return false;
}
DICOMRTMimeTypes::RTStructMimeType* DICOMRTMimeTypes::RTStructMimeType::Clone() const
{
return new RTStructMimeType(*this);
}
DICOMRTMimeTypes::RTPlanMimeType::RTPlanMimeType()
: CustomMimeType(DICOMRT_PLAN_MIMETYPE_NAME())
{
std::string category = "DICOMRT";
this->SetCategory(category);
this->SetComment("RTPLAN");
this->AddExtension("dcm");
}
bool DICOMRTMimeTypes::RTPlanMimeType::AppliesTo(const std::string &path) const
{
bool canRead(CustomMimeType::AppliesTo(path));
if (!canRead) {
return false;
}
// fix for bug 18572
// Currently this function is called for writing as well as reading, in that case
- // the image information can of course not be parsed or further identifyed.
- //so as a work arround we just return the current canRead if the file does not exist.
+ // the image information can of course not be parsed or further identified.
+ //so as a work around we just return the current canRead if the file does not exist.
if (!itksys::SystemTools::FileExists(path.c_str()))
{
return canRead;
}
// end fix for bug 18572
auto modality = GetModality(path);
if (modality == "RTPLAN") {
return true;
}
return false;
}
DICOMRTMimeTypes::RTPlanMimeType* DICOMRTMimeTypes::RTPlanMimeType::Clone() const
{
return new RTPlanMimeType(*this);
}
DICOMRTMimeTypes::RTDoseMimeType DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE()
{
return RTDoseMimeType();
}
DICOMRTMimeTypes::RTStructMimeType DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE()
{
return RTStructMimeType();
}
DICOMRTMimeTypes::RTPlanMimeType DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE()
{
return RTPlanMimeType();
}
// Names
std::string DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".dicomrt.dose";
}
std::string DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".dicomrt.struct";
}
std::string DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".dicomrt.plan";
}
// Descriptions
std::string DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE_DESCRIPTION()
{
return "RTDOSE reader";
}
std::string DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE_DESCRIPTION()
{
return "RTSTRUCT reader";
}
std::string DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE_DESCRIPTION()
{
return "RTPLAN reader";
}
}
diff --git a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
index e3698ba136..083611bf1a 100644
--- a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
+++ b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
@@ -1,1163 +1,1163 @@
/*============================================================================
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 "mitkImageStatisticsHolder.h"
#include "mitkPlaneClipping.h"
#include "mitkPropertyNameHelper.h"
#include <mitkAbstractTransformGeometry.h>
#include <mitkDataNode.h>
#include <mitkImageSliceSelector.h>
#include <mitkIsoDoseLevelSetProperty.h>
#include <mitkIsoDoseLevelVectorProperty.h>
#include <mitkLevelWindowProperty.h>
#include <mitkLookupTableProperty.h>
#include <mitkPixelType.h>
#include <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkRTConstants.h>
#include <mitkRenderingModeProperty.h>
#include <mitkResliceMethodProperty.h>
#include <mitkTransferFunctionProperty.h>
#include <mitkVtkResliceInterpolationProperty.h>
// MITK Rendering
#include "mitkDoseImageVtkMapper2D.h"
#include "vtkMitkLevelWindowFilter.h"
#include "vtkMitkThickSlicesFilter.h"
#include "vtkNeverTranslucentTexture.h"
// VTK
#include <vtkCamera.h>
#include <vtkCellData.h>
#include <vtkColorTransferFunction.h>
#include <vtkGeneralTransform.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkMatrix4x4.h>
#include <vtkPlaneSource.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkTransform.h>
#include <vtkUnsignedCharArray.h>
// ITK
#include <itkRGBAPixel.h>
mitk::DoseImageVtkMapper2D::DoseImageVtkMapper2D()
{
}
mitk::DoseImageVtkMapper2D::~DoseImageVtkMapper2D()
{
// 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::DoseImageVtkMapper2D::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 sagittal) 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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::GetInput(void)
{
return static_cast<const mitk::Image *>(GetDataNode()->GetData());
}
vtkProp *mitk::DoseImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
void mitk::DoseImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::Image *input = const_cast<mitk::Image *>(this->GetInput());
mitk::DataNode *datanode = this->GetDataNode();
if (input == nullptr || input->IsInitialized() == false)
{
return;
}
// check if there is a valid worldGeometry
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry()))
{
return;
}
input->Update();
// 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, input->GetSlicedGeometry()))
{
// 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);
return;
}
// set main input for ExtractSliceFilter
localStorage->m_Reslicer->SetInput(input);
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(
input->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 ((input->GetDimension() >= 3) && (input->GetDimension(2) > 1))
{
VtkResliceInterpolationProperty *resliceInterpolationProperty;
datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation");
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
+ // set the vtk output property to true, makes sure that no unneeded mitk image conversion
// is done.
localStorage->m_Reslicer->SetVtkOutputRequest(true);
// Thickslicing
int thickSlicesMode = 0;
int thickSlicesNum = 1;
// Thick slices parameters
if (input->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") && resliceMethodEnumProperty)
thickSlicesMode = resliceMethodEnumProperty->GetValueAsId();
IntProperty *intProperty = nullptr;
if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty)
{
thickSlicesNum = intProperty->GetValue();
if (thickSlicesNum < 1)
thickSlicesNum = 1;
if (thickSlicesNum > 10)
thickSlicesNum = 10;
}
}
else
{
MITK_WARN << "no associated widget plane data tree node found";
}
}
const PlaneGeometry *planeGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry);
if (thickSlicesMode > 0)
{
double dataZSpacing = 1.0;
Vector3D normInIndex, normal;
if (planeGeometry != nullptr)
{
normal = planeGeometry->GetNormal();
}
else
{
const mitk::AbstractTransformGeometry *abstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(worldGeometry);
if (abstractGeometry != nullptr)
normal = abstractGeometry->GetPlane()->GetNormal();
else
return; // no fitting geometry set
}
normal.Normalize();
input->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
+ // this is needed when thick mode was enable before. 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
+ // Bounds information for reslicing (only required if reference geometry
// is present)
// this used for generating a vtkPLaneSource with the right size
double sliceBounds[6];
for (int i = 0; i < 6; ++i)
{
sliceBounds[i] = 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 (int i = 0; i < 6; ++i)
{
textureClippingBounds[i] = 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(input->GetGeometry(), planeGeometry, textureClippingBounds);
textureClippingBounds[0] = static_cast<int>(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[1] = static_cast<int>(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5);
textureClippingBounds[2] = static_cast<int>(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5);
textureClippingBounds[3] = static_cast<int>(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 showIsoLines property
bool showIsoLines = false;
datanode->GetBoolProperty("dose.showIsoLines", showIsoLines, renderer);
if (showIsoLines) // contour rendering
{
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer);
float binaryOutlineWidth(1.0);
if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer))
{
if (localStorage->m_Actors->GetNumberOfPaths() > 1)
{
float binaryOutlineShadowWidth(1.5);
datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer);
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))
->GetProperty()
->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth);
}
localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth);
}
}
else
{
localStorage->m_ReslicedImage = nullptr;
localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData);
return;
}
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);
vtkActor *contourShadowActor = dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0));
if (showIsoLines) // 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_Actor->SetTexture(nullptr); // no texture for contours
bool binaryOutlineShadow(false);
datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer);
if (binaryOutlineShadow)
contourShadowActor->SetVisibility(true);
else
contourShadowActor->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_Actor->SetTexture(localStorage->m_Texture);
contourShadowActor->SetVisibility(false);
}
// We have been modified => save this for next Update()
localStorage->m_LastUpdateTime.Modified();
}
void mitk::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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;
GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer);
GetDataNode()->GetBoolProperty("selected", selected, renderer);
if (hover && !selected)
{
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
else
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
}
if (selected)
{
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
else
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
}
if (!hover && !selected)
{
GetDataNode()->GetColor(rgb, renderer, "color");
}
double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv);
localStorage->m_Actor->GetProperty()->SetColor(rgbConv);
if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1)
{
float rgb[3] = {1.0f, 1.0f, 1.0f};
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("outline binary shadow color", renderer));
if (colorprop.IsNotNull())
{
memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float));
}
double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv);
}
}
void mitk::DoseImageVtkMapper2D::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_Actor->GetProperty()->SetOpacity(opacity);
if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1)
{
dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0))
->GetProperty()
->SetOpacity(opacity);
}
}
void mitk::DoseImageVtkMapper2D::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<mitk::RenderingModeProperty *>(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::DoseImageVtkMapper2D::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<mitk::LookupTableProperty *>(this->GetDataNode()->GetProperty("LookupTable"));
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.
+ // in every iteration of the rendering.
}
localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable);
}
void mitk::DoseImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer)
{
mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty *>(
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());
}
void mitk::DoseImageVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
{
return;
}
mitk::Image *data = const_cast<mitk::Image *>(this->GetInput());
if (data == nullptr)
{
return;
}
// Calculate time step of the input data for the specified renderer (integer value)
this->CalculateTimeStep(renderer);
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) ||
(!dataTimeGeometry->IsValidTimeStep(this->GetTimestep())))
{
return;
}
const DataNode *node = this->GetDataNode();
data->UpdateOutputInformation();
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// check if something important has changed and we need to rerender
if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified?
||
(localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified?
||
(localStorage->m_LastUpdateTime <
renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified?
||
(localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified?
||
(localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->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::DoseImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
mitk::Image::Pointer image = dynamic_cast<mitk::Image *>(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)); // default value
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);
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);
}
// Otherwise do nothing - the default grayscale look-up table has already been set
}
bool isBinaryImage(false);
if (!node->GetBoolProperty("binary", isBinaryImage))
{
// ok, property is not set, use heuristic to determine if this
// is a binary image
mitk::Image::Pointer centralSliceImage;
ScalarType minValue = 0.0;
ScalarType maxValue = 0.0;
ScalarType min2ndValue = 0.0;
ScalarType max2ndValue = 0.0;
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();
if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized())
{
minValue = centralSliceImage->GetStatistics()->GetScalarValueMin();
maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax();
min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin();
max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax();
}
if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue)
{
// centralSlice is strange, lets look at all data
minValue = image->GetStatistics()->GetScalarValueMin();
maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute();
min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute();
max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute();
}
isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue);
}
// 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);
std::string className = image->GetNameOfClass();
if (className != "TensorImage" && className != "OdfImage" && className != "ShImage")
{
PixelType pixelType = image->GetPixelType();
size_t numComponents = pixelType.GetNumberOfComponents();
if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 ||
numComponents > 4)
node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite);
}
}
if (image.IsNotNull() && image->IsInitialized())
{
if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr))
{
/* initialize level/window from DICOM tags */
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());
mitk::LevelWindow contrast;
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<mitk::Image *>(node->GetData()), false, true); // we need this as a fallback
}
contrast.SetLevelWindow(level, window, true);
node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer);
}
}
if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) &&
(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::DoseImageVtkMapper2D::LocalStorage *mitk::DoseImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
vtkSmartPointer<vtkPolyData> mitk::DoseImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer)
{
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points
vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3);
colors->SetName("Colors");
float pref;
this->GetDataNode()->GetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), pref);
mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast<mitk::IsoDoseLevelSetProperty *>(
GetDataNode()->GetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str()));
mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = propIsoSet->GetValue();
for (mitk::IsoDoseLevelSet::ConstIterator doseIT = isoDoseLevelSet->Begin(); doseIT != isoDoseLevelSet->End();
++doseIT)
{
if (doseIT->GetVisibleIsoLine())
{
this->CreateLevelOutline(renderer, &(doseIT.Value()), pref, points, lines, colors);
} // end of if visible dose value
} // end of loop over all does values
mitk::IsoDoseLevelVectorProperty::Pointer propfreeIsoVec = dynamic_cast<mitk::IsoDoseLevelVectorProperty *>(
GetDataNode()->GetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str()));
mitk::IsoDoseLevelVector::Pointer frereIsoDoseLevelVec = propfreeIsoVec->GetValue();
for (mitk::IsoDoseLevelVector::ConstIterator freeDoseIT = frereIsoDoseLevelVec->Begin();
freeDoseIT != frereIsoDoseLevelVec->End();
++freeDoseIT)
{
if (freeDoseIT->Value()->GetVisibleIsoLine())
{
this->CreateLevelOutline(renderer, freeDoseIT->Value(), pref, points, lines, colors);
} // end of if visible dose value
} // end of loop over all does values
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// Add the points to the dataset
polyData->SetPoints(points);
// Add the lines to the dataset
polyData->SetLines(lines);
polyData->GetCellData()->SetScalars(colors);
return polyData;
}
void mitk::DoseImageVtkMapper2D::CreateLevelOutline(mitk::BaseRenderer *renderer,
const mitk::IsoDoseLevel *level,
float pref,
vtkSmartPointer<vtkPoints> points,
vtkSmartPointer<vtkCellArray> lines,
vtkSmartPointer<vtkUnsignedCharArray> colors)
{
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?
// get the depth for each contour
float depth = CalculateLayerDepth(renderer);
double doseValue = level->GetDoseValue() * pref;
mitk::IsoDoseLevel::ColorType isoColor = level->GetColor();
unsigned char colorLine[3] = {static_cast<unsigned char>(isoColor.GetRed() * 255),
static_cast<unsigned char>(isoColor.GetGreen() * 255),
static_cast<unsigned char>(isoColor.GetBlue() * 255)};
int x = xMin; // pixel index x
int y = yMin; // pixel index y
float *currentPixel;
// We take the pointer to the first pixel of the image
currentPixel = static_cast<float *>(localStorage->m_ReslicedImage->GetScalarPointer());
if (!currentPixel){
mitkThrow() << "currentPixel invalid";
}
while (y <= yMax)
{
// if the current pixel value is set to something
if ((currentPixel) && (*currentPixel >= doseValue))
{
// 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) < doseValue)
{ // 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);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv not the last line vvvvv
if (y < yMax && *(currentPixel + line) < doseValue)
{ // 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);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv not the first pixel vvvvv
if ((x > xMin || y > yMin) && *(currentPixel - 1) < doseValue)
{ // 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);
colors->InsertNextTypedTuple(colorLine);
}
// if vvvvv not the last pixel vvvvv
if ((y < yMax || (x < xMax)) && *(currentPixel + 1) < doseValue)
{ // 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);
colors->InsertNextTypedTuple(colorLine);
}
/* 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);
colors->InsertNextTypedTuple(colorLine);
}
// 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);
colors->InsertNextTypedTuple(colorLine);
}
// 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);
colors->InsertNextTypedTuple(colorLine);
}
// 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);
colors->InsertNextTypedTuple(colorLine);
}
} // 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
}
void mitk::DoseImageVtkMapper2D::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 sagittal
vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_Reslicer->GetResliceAxes();
trans->SetMatrix(matrix);
// transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal)
localStorage->m_Actor->SetUserTransform(trans);
// transform the origin to center based coordinates, because MITK is center based.
localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
if (localStorage->m_Actors->GetNumberOfPaths() > 1)
{
vtkActor *secondaryActor = dynamic_cast<vtkActor *>(localStorage->m_Actors->GetParts()->GetItemAsObject(0));
secondaryActor->SetUserTransform(trans);
secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
}
bool mitk::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::DoseImageVtkMapper2D::LocalStorage::LocalStorage()
: m_VectorComponentExtractor(vtkSmartPointer<vtkImageExtractComponents>::New())
{
m_LevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
// Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
m_Texture = vtkSmartPointer<vtkNeverTranslucentTexture>::New().GetPointer();
m_DefaultLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_BinaryLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_ColorLookupTable = vtkSmartPointer<vtkLookupTable>::New();
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_Reslicer = mitk::ExtractSliceFilter::New();
m_TSFilter = vtkSmartPointer<vtkMitkThickSlicesFilter>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_ReslicedImage = vtkSmartPointer<vtkImageData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::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_Actor->SetMapper(m_Mapper);
vtkSmartPointer<vtkActor> outlineShadowActor = vtkSmartPointer<vtkActor>::New();
outlineShadowActor->SetMapper(m_Mapper);
m_Actors->AddPart(outlineShadowActor);
m_Actors->AddPart(m_Actor);
}
diff --git a/Modules/RTUI/Helper/mitkRTUIConstants.h b/Modules/RTUI/Helper/mitkRTUIConstants.h
index e0d280938b..b0b9b78aed 100644
--- a/Modules/RTUI/Helper/mitkRTUIConstants.h
+++ b/Modules/RTUI/Helper/mitkRTUIConstants.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 mitkRTUIConstants_h
#define mitkRTUIConstants_h
#include <string>
#include <mitkDoseValueType.h>
#include "MitkRTUIExports.h"
namespace mitk
{
struct MITKRTUI_EXPORT RTUIConstants
{
/** ID/Path of main preference node for RT UI. */
static const std::string ROOT_PREFERENCE_NODE_ID;
- /** Bool that indicates how the prescribed dose should be defined, if unkown. True: UNKNOWN_PRESCRIBED_DOSE_HANDLING_VALUE should be used as
- default dose value in Gy; False: it should be used as fraction of the max dose to determin the prescribed dose.*/
+ /** Bool that indicates how the prescribed dose should be defined, if unknown. True: UNKNOWN_PRESCRIBED_DOSE_HANDLING_VALUE should be used as
+ default dose value in Gy; False: it should be used as fraction of the max dose to determine the prescribed dose.*/
static const std::string UNKNOWN_PRESCRIBED_DOSE_HANDLING_AS_DEFAULT_ID;
- /** Value that is used to determin unknown prescribed doses.*/
+ /** Value that is used to determine unknown prescribed doses.*/
static const std::string UNKNOWN_PRESCRIBED_DOSE_HANDLING_VALUE_ID;
/** ID/Path of main preference node where all iso dose level presets are stored (e.g. ROOT_ISO_PRESETS_PREFERENCE_NODE_ID+"/[Preset1]"). */
static const std::string ROOT_ISO_PRESETS_PREFERENCE_NODE_ID;
/** ID/Path of main preference for dose visualization preferences. */
static const std::string ROOT_DOSE_VIS_PREFERENCE_NODE_ID;
/** ID for the reference dose stored as preference. */
static const std::string REFERENCE_DOSE_ID;
/** ID for the preference flag that indicates if the reference dose is synced for all nodes*/
static const std::string GLOBAL_REFERENCE_DOSE_SYNC_ID;
- /** ID for the flag if dose should be displayed as absoulte dose. */
+ /** ID for the flag if dose should be displayed as absolute dose. */
static const std::string DOSE_DISPLAY_ABSOLUTE_ID;
- /** ID for the global visiblity switch for iso line visualization. */
+ /** ID for the global visibility switch for iso line visualization. */
static const std::string GLOBAL_VISIBILITY_ISOLINES_ID;
- /** ID for the global visiblity switch for color wash visualization. */
+ /** ID for the global visibility switch for color wash visualization. */
static const std::string GLOBAL_VISIBILITY_COLORWASH_ID;
/** ID for the selected iso preset that should be used (value of ROOT_ISO_PRESETS_PREFERENCE_NODE_ID + value of this key can
be used to construct the passed to the selected preset. */
static const std::string SELECTED_ISO_PRESET_ID;
/** ID for the relative dose value of an iso dose level. */
static const std::string ISO_LEVEL_DOSE_VALUE_ID;
/** ID for the color (red component) of an iso dose level. */
static const std::string ISO_LEVEL_COLOR_RED_ID;
/** ID for the color (green component) of an iso dose level. */
static const std::string ISO_LEVEL_COLOR_GREEN_ID;
/** ID for the color (blue component) of an iso dose level. */
static const std::string ISO_LEVEL_COLOR_BLUE_ID;
- /** ID for the visiblity switch for iso line visualization. */
+ /** ID for the visibility switch for iso line visualization. */
static const std::string ISO_LEVEL_VISIBILITY_ISOLINES_ID;
- /** ID for the visiblity switch for color wash visualization. */
+ /** ID for the visibility switch for color wash visualization. */
static const std::string ISO_LEVEL_VISIBILITY_COLORWASH_ID;
/** Default value used as reference_dose_if not defined by application or data node*/
static const DoseValueAbs DEFAULT_REFERENCE_DOSE_VALUE;
};
struct MITKRTUI_EXPORT RTCTKEventConstants
{
/** ID/Path of main preference node for RT UI. */
static const std::string TOPIC_REFERENCE_DOSE;
static const std::string TOPIC_REFERENCE_DOSE_CHANGED;
static const std::string TOPIC_ISO_DOSE_LEVEL_PRESETS;
static const std::string TOPIC_ISO_DOSE_LEVEL_PRESETS_CHANGED;
static const std::string TOPIC_GLOBAL_VISIBILITY_CHANGED;
};
}
#endif
diff --git a/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h b/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h
index afca7d0327..959cdeff2e 100644
--- a/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h
+++ b/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h
@@ -1,93 +1,93 @@
/*============================================================================
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 QmitkIsoDoseLevelSetModel_h
#define QmitkIsoDoseLevelSetModel_h
#include <QAbstractTableModel>
#include "mitkIsoDoseLevelCollections.h"
#include "MitkRTUIExports.h"
/*!
\class QmitkIsoDoseLevelSetModel
Model that handles a iso dose level set and allows viewing and editing of its contents.
Please see special delegates (QmitkDoseColorDelegate, QmitkDoseValueDelegate, QmitkDoseVisualStyleDelegate) to
handle visualization and editing in views that work on this model.
\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation.
*/
class MITKRTUI_EXPORT QmitkIsoDoseLevelSetModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit QmitkIsoDoseLevelSetModel(QObject *parent = nullptr);
~QmitkIsoDoseLevelSetModel() override {};
/** Sets the data handled by the model and resets the modified flag*/
void setIsoDoseLevelSet(mitk::IsoDoseLevelSet *pSet);
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool getShowAbsoluteDose() const;
mitk::DoseValueAbs getReferenceDose() const;
bool getVisibilityEditOnly() const;
void switchVisibilityIsoLines(bool activate);
void switchVisibilityColorWash(bool activate);
void invertVisibilityIsoLines();
void invertVisibilityColorWash();
void swapVisibility();
void addLevel();
void deleteLevel(const QModelIndex &index);
/**Indicates if the content of the model was modified since the data was set via setIsoDoseLevelSet()*/
bool isModified();
public Q_SLOTS:
/**
* \brief Slot that can be used to set the prescribed dose.
*/
void setReferenceDose(double newReferenceDose);
/**
* \brief Slot that can be used to adjust whether the dose should be displayed in absolute or relative units.
*/
void setShowAbsoluteDose(bool showAbsoluteDose);
/**
- * \brief Slat that can be used to adjust wether the model allows to edit only visibilities (no dose value or color)
+ * \brief Slat that can be used to adjust whether the model allows to edit only visibilities (no dose value or color)
*/
void setVisibilityEditOnly(bool onlyVisibility);
private:
mitk::IsoDoseLevelSet::Pointer m_DoseSet;
bool m_showAbsoluteDose;
bool m_visibilityEditOnly;
mitk::DoseValueAbs m_referenceDose;
/** Indicates if the data of the model was modified, since the model was set. */
bool m_modified;
};
#endif
diff --git a/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h b/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h
index a42d01afd6..d7574701ea 100644
--- a/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h
+++ b/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h
@@ -1,39 +1,39 @@
/*============================================================================
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 mitkGeometryDataSerializer_h
#define mitkGeometryDataSerializer_h
#include "mitkBaseDataSerializer.h"
namespace mitk
{
/**
\brief Serializes mitk::GeometryData for mitk::SceneIO.
\warning depends on mitk::GeometryDataWriterService which is first implemented only for the Geometry3D class!
- See current status of that class if you want to use other geomety types.
+ See current status of that class if you want to use other geometry types.
*/
class GeometryDataSerializer : public BaseDataSerializer
{
public:
mitkClassMacro(GeometryDataSerializer, BaseDataSerializer);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self) std::string Serialize() override;
protected:
GeometryDataSerializer();
~GeometryDataSerializer() override;
};
} // namespace
#endif
diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp
index 6072a7bc3f..53517d2563 100644
--- a/Modules/SceneSerialization/src/mitkSceneIO.cpp
+++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp
@@ -1,578 +1,578 @@
/*============================================================================
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 <Poco/Delegate.h>
#include <Poco/Path.h>
#include <Poco/TemporaryFile.h>
#include <Poco/Zip/Compress.h>
#include <Poco/Zip/Decompress.h>
#include "mitkBaseDataSerializer.h"
#include "mitkPropertyListSerializer.h"
#include "mitkSceneIO.h"
#include "mitkSceneReader.h"
#include "mitkBaseRenderer.h"
#include "mitkProgressBar.h"
#include "mitkRenderingManager.h"
#include "mitkStandaloneDataStorage.h"
#include <mitkLocaleSwitch.h>
#include <mitkStandardFileLocations.h>
#include <mitkUIDGenerator.h>
#include <itkObjectFactoryBase.h>
#include <fstream>
#include <mitkIOUtil.h>
#include <sstream>
#include "itksys/SystemTools.hxx"
#include <tinyxml2.h>
mitk::SceneIO::SceneIO() : m_WorkingDirectory(""), m_UnzipErrors(0)
{
}
mitk::SceneIO::~SceneIO()
{
}
std::string mitk::SceneIO::CreateEmptyTempDirectory()
{
mitk::UIDGenerator uidGen;
// std::string returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() +
// Poco::Path::separator() + "SceneIOTemp" + uidGen.GetUID();
std::string returnValue = Poco::Path::temp() + "SceneIOTemp" + uidGen.GetUID();
std::string uniquename = returnValue + Poco::Path::separator();
Poco::File tempdir(uniquename);
try
{
bool existsNot = tempdir.createDirectory();
if (!existsNot)
{
- MITK_ERROR << "Warning: Directory already exitsts: " << uniquename << " (choosing another)";
+ MITK_ERROR << "Warning: Directory already exists: " << uniquename << " (choosing another)";
returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + Poco::Path::separator() +
"SceneIOTempDirectory" + uidGen.GetUID();
uniquename = returnValue + Poco::Path::separator();
Poco::File tempdir2(uniquename);
if (!tempdir2.createDirectory())
{
- MITK_ERROR << "Warning: Second directory also already exitsts: " << uniquename;
+ MITK_ERROR << "Warning: Second directory also already exists: " << uniquename;
}
}
}
catch (std::exception &e)
{
MITK_ERROR << "Could not create temporary directory " << uniquename << ":" << e.what();
return "";
}
return returnValue;
}
mitk::DataStorage::Pointer mitk::SceneIO::LoadScene(const std::string &filename,
DataStorage *pStorage,
bool clearStorageFirst)
{
mitk::LocaleSwitch localeSwitch("C");
// prepare data storage
DataStorage::Pointer storage = pStorage;
if (storage.IsNull())
{
storage = StandaloneDataStorage::New().GetPointer();
}
// test input filename
if (filename.empty())
{
MITK_ERROR << "No filename given. Not possible to load scene.";
return storage;
}
// test if filename can be read
std::ifstream file(filename.c_str(), std::ios::binary);
if (!file.good())
{
MITK_ERROR << "Cannot open '" << filename << "' for reading";
return storage;
}
// get new temporary directory
m_WorkingDirectory = CreateEmptyTempDirectory();
if (m_WorkingDirectory.empty())
{
MITK_ERROR << "Could not create temporary directory. Cannot open scene files.";
return storage;
}
// unzip all filenames contents to temp dir
m_UnzipErrors = 0;
Poco::Zip::Decompress unzipper(file, Poco::Path(m_WorkingDirectory));
unzipper.EError += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>(
this, &SceneIO::OnUnzipError);
unzipper.EOk += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>(
this, &SceneIO::OnUnzipOk);
unzipper.decompressAllFiles();
unzipper.EError -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>(
this, &SceneIO::OnUnzipError);
unzipper.EOk -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>(
this, &SceneIO::OnUnzipOk);
if (m_UnzipErrors)
{
MITK_ERROR << "There were " << m_UnzipErrors << " errors unzipping '" << filename
<< "'. Will attempt to read whatever could be unzipped.";
}
// transcode locale-dependent string
m_WorkingDirectory = Poco::Path::transcode (m_WorkingDirectory);
auto indexFile = m_WorkingDirectory + mitk::IOUtil::GetDirectorySeparator() + "index.xml";
storage = LoadSceneUnzipped(indexFile, storage, clearStorageFirst);
// delete temp directory
try
{
Poco::File deleteDir(m_WorkingDirectory);
deleteDir.remove(true); // recursive
}
catch (...)
{
MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory;
}
- // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method)
+ // return new data storage, even if empty or incomplete (return as much as possible but notify calling method)
return storage;
}
mitk::DataStorage::Pointer mitk::SceneIO::LoadSceneUnzipped(const std::string &indexfilename,
DataStorage *pStorage,
bool clearStorageFirst)
{
mitk::LocaleSwitch localeSwitch("C");
// prepare data storage
DataStorage::Pointer storage = pStorage;
if (storage.IsNull())
{
storage = StandaloneDataStorage::New().GetPointer();
}
if (clearStorageFirst)
{
try
{
storage->Remove(storage->GetAll());
}
catch (...)
{
MITK_ERROR << "DataStorage cannot be cleared properly.";
}
}
// test input filename
if (indexfilename.empty())
{
MITK_ERROR << "No filename given. Not possible to load scene.";
return storage;
}
// transcode locale-dependent string
std::string tempfilename;
std::string workingDir;
itksys::SystemTools::SplitProgramPath(indexfilename, workingDir, tempfilename);
// test if index.xml exists
// parse index.xml with TinyXML
tinyxml2::XMLDocument document;
if (tinyxml2::XML_SUCCESS != document.LoadFile(indexfilename.c_str()))
{
MITK_ERROR << "Could not open/read/parse " << workingDir << mitk::IOUtil::GetDirectorySeparator()
<< "index.xml\nTinyXML reports: " << document.ErrorStr() << std::endl;
return storage;
}
SceneReader::Pointer reader = SceneReader::New();
if (!reader->LoadScene(document, workingDir, storage))
{
MITK_ERROR << "There were errors while loading scene file " << indexfilename << ". Your data may be corrupted";
}
- // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method)
+ // return new data storage, even if empty or incomplete (return as much as possible but notify calling method)
return storage;
}
bool mitk::SceneIO::SaveScene(DataStorage::SetOfObjects::ConstPointer sceneNodes,
const DataStorage *storage,
const std::string &filename)
{
if (!sceneNodes)
{
MITK_ERROR << "No set of nodes given. Not possible to save scene.";
return false;
}
if (!storage)
{
MITK_ERROR << "No data storage given. Not possible to save scene."; // \TODO: Technically, it would be possible to
// save the nodes without their relation
return false;
}
if (filename.empty())
{
MITK_ERROR << "No filename given. Not possible to save scene.";
return false;
}
mitk::LocaleSwitch localeSwitch("C");
try
{
m_FailedNodes = DataStorage::SetOfObjects::New();
m_FailedProperties = PropertyList::New();
// start XML DOM
tinyxml2::XMLDocument document;
document.InsertEndChild(document.NewDeclaration());
auto *version = document.NewElement("Version");
version->SetAttribute("Writer", __FILE__);
version->SetAttribute("Revision", "$Revision: 17055 $");
version->SetAttribute("FileVersion", 1);
document.InsertEndChild(version);
// DataStorage::SetOfObjects::ConstPointer sceneNodes = storage->GetSubset( predicate );
if (sceneNodes.IsNull())
{
MITK_WARN << "Saving empty scene to " << filename;
}
else
{
if (sceneNodes->size() == 0)
{
MITK_WARN << "Saving empty scene to " << filename;
}
MITK_INFO << "Storing scene with " << sceneNodes->size() << " objects to " << filename;
m_WorkingDirectory = CreateEmptyTempDirectory();
if (m_WorkingDirectory.empty())
{
MITK_ERROR << "Could not create temporary directory. Cannot create scene files.";
return false;
}
ProgressBar::GetInstance()->AddStepsToDo(sceneNodes->size());
// find out about dependencies
typedef std::map<DataNode *, std::string> UIDMapType;
typedef std::map<DataNode *, std::list<std::string>> SourcesMapType;
UIDMapType nodeUIDs; // for dependencies: ID of each node
SourcesMapType sourceUIDs; // for dependencies: IDs of a node's parent nodes
UIDGenerator nodeUIDGen("OBJECT_");
for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter)
{
DataNode *node = iter->GetPointer();
if (!node)
continue; // unlikely event that we get a nullptr pointer as an object for saving. just ignore
// generate UIDs for all source objects
DataStorage::SetOfObjects::ConstPointer sourceObjects = storage->GetSources(node);
for (auto sourceIter = sourceObjects->begin();
sourceIter != sourceObjects->end();
++sourceIter)
{
if (std::find(sceneNodes->begin(), sceneNodes->end(), *sourceIter) == sceneNodes->end())
continue; // source is not saved, so don't generate a UID for this source
// create a uid for the parent object
if (nodeUIDs[*sourceIter].empty())
{
nodeUIDs[*sourceIter] = nodeUIDGen.GetUID();
}
// store this dependency for writing
sourceUIDs[node].push_back(nodeUIDs[*sourceIter]);
}
if (nodeUIDs[node].empty())
{
nodeUIDs[node] = nodeUIDGen.GetUID();
}
}
// write out objects, dependencies and properties
for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter)
{
DataNode *node = iter->GetPointer();
if (node)
{
auto *nodeElement = document.NewElement("node");
std::string filenameHint(node->GetName());
filenameHint = itksys::SystemTools::MakeCindentifier(
filenameHint.c_str()); // escape filename <-- only allow [A-Za-z0-9_], replace everything else with _
// store dependencies
auto searchUIDIter = nodeUIDs.find(node);
if (searchUIDIter != nodeUIDs.end())
{
// store this node's ID
nodeElement->SetAttribute("UID", searchUIDIter->second.c_str());
}
auto searchSourcesIter = sourceUIDs.find(node);
if (searchSourcesIter != sourceUIDs.end())
{
// store all source IDs
for (auto sourceUIDIter = searchSourcesIter->second.begin();
sourceUIDIter != searchSourcesIter->second.end();
++sourceUIDIter)
{
auto *uidElement = document.NewElement("source");
uidElement->SetAttribute("UID", sourceUIDIter->c_str());
nodeElement->InsertEndChild(uidElement);
}
}
// store basedata
if (BaseData *data = node->GetData())
{
// std::string filenameHint( node->GetName() );
bool error(false);
auto *dataElement = SaveBaseData(document, data, filenameHint, error); // returns a reference to a file
if (error)
{
m_FailedNodes->push_back(node);
}
// store basedata properties
PropertyList *propertyList = data->GetPropertyList();
if (propertyList && !propertyList->IsEmpty())
{
auto *baseDataPropertiesElement =
SavePropertyList(document, propertyList, filenameHint + "-data"); // returns a reference to a file
dataElement->InsertEndChild(baseDataPropertiesElement);
}
nodeElement->InsertEndChild(dataElement);
}
// store all renderwindow specific propertylists
mitk::DataNode::PropertyListKeyNames propertyListKeys = node->GetPropertyListNames();
for (const auto &renderWindowName : propertyListKeys)
{
PropertyList *propertyList = node->GetPropertyList(renderWindowName);
if (propertyList && !propertyList->IsEmpty())
{
auto *renderWindowPropertiesElement =
SavePropertyList(document, propertyList, filenameHint + "-" + renderWindowName); // returns a reference to a file
renderWindowPropertiesElement->SetAttribute("renderwindow", renderWindowName.c_str());
nodeElement->InsertEndChild(renderWindowPropertiesElement);
}
}
// don't forget the renderwindow independent list
PropertyList *propertyList = node->GetPropertyList();
if (propertyList && !propertyList->IsEmpty())
{
auto *propertiesElement =
SavePropertyList(document, propertyList, filenameHint + "-node"); // returns a reference to a file
nodeElement->InsertEndChild(propertiesElement);
}
document.InsertEndChild(nodeElement);
}
else
{
MITK_WARN << "Ignoring nullptr node during scene serialization.";
}
ProgressBar::GetInstance()->Progress();
} // end for all nodes
} // end if sceneNodes
std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory );
auto xmlFilename = defaultLocale_WorkingDirectory + Poco::Path::separator() + "index.xml";
if (tinyxml2::XML_SUCCESS != document.SaveFile(xmlFilename.c_str()))
{
MITK_ERROR << "Could not write scene to " << defaultLocale_WorkingDirectory << Poco::Path::separator() << "index.xml"
<< "\nTinyXML reports '" << document.ErrorStr() << "'";
return false;
}
else
{
try
{
Poco::File deleteFile(filename.c_str());
if (deleteFile.exists())
{
deleteFile.remove();
}
// create zip at filename
std::ofstream file(filename.c_str(), std::ios::binary | std::ios::out);
if (!file.good())
{
MITK_ERROR << "Could not open a zip file for writing: '" << filename << "'";
return false;
}
else
{
Poco::Zip::Compress zipper(file, true);
Poco::Path tmpdir(m_WorkingDirectory);
zipper.addRecursive(tmpdir);
zipper.close();
}
try
{
Poco::File deleteDir(m_WorkingDirectory);
deleteDir.remove(true); // recursive
}
catch (...)
{
MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory;
return false; // ok?
}
}
catch (std::exception &e)
{
MITK_ERROR << "Could not create ZIP file from " << m_WorkingDirectory << "\nReason: " << e.what();
return false;
}
return true;
}
}
catch (std::exception &e)
{
MITK_ERROR << "Caught exception during saving temporary files to disk. Error description: '" << e.what() << "'";
return false;
}
}
tinyxml2::XMLElement *mitk::SceneIO::SaveBaseData(tinyxml2::XMLDocument &doc, BaseData *data, const std::string &filenamehint, bool &error)
{
assert(data);
error = true;
// find correct serializer
// the serializer must
// - create a file containing all information to recreate the BaseData object --> needs to know where to put this
// file (and a filename?)
// - TODO what to do about writers that creates one file per timestep?
auto *element = doc.NewElement("data");
element->SetAttribute("type", data->GetNameOfClass());
// construct name of serializer class
std::string serializername(data->GetNameOfClass());
serializername += "Serializer";
std::list<itk::LightObject::Pointer> thingsThatCanSerializeThis =
itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str());
if (thingsThatCanSerializeThis.size() < 1)
{
MITK_ERROR << "No serializer found for " << data->GetNameOfClass() << ". Skipping object";
}
for (auto iter = thingsThatCanSerializeThis.begin();
iter != thingsThatCanSerializeThis.end();
++iter)
{
if (auto *serializer = dynamic_cast<BaseDataSerializer *>(iter->GetPointer()))
{
serializer->SetData(data);
serializer->SetFilenameHint(filenamehint);
std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory );
serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory);
try
{
std::string writtenfilename = serializer->Serialize();
element->SetAttribute("file", writtenfilename.c_str());
if (!writtenfilename.empty())
error = false;
}
catch (std::exception &e)
{
MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what();
}
break;
}
}
element->SetAttribute("UID", data->GetUID().c_str());
return element;
}
tinyxml2::XMLElement *mitk::SceneIO::SavePropertyList(tinyxml2::XMLDocument &doc, PropertyList *propertyList, const std::string &filenamehint)
{
assert(propertyList);
// - TODO what to do about shared properties (same object in two lists or behind several keys)?
auto *element = doc.NewElement("properties");
// construct name of serializer class
PropertyListSerializer::Pointer serializer = PropertyListSerializer::New();
serializer->SetPropertyList(propertyList);
serializer->SetFilenameHint(filenamehint);
std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory );
serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory);
try
{
std::string writtenfilename = serializer->Serialize();
element->SetAttribute("file", writtenfilename.c_str());
PropertyList::Pointer failedProperties = serializer->GetFailedProperties();
if (failedProperties.IsNotNull())
{
// move failed properties to global list
m_FailedProperties->ConcatenatePropertyList(failedProperties, true);
}
}
catch (std::exception &e)
{
MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what();
}
return element;
}
const mitk::SceneIO::FailedBaseDataListType *mitk::SceneIO::GetFailedNodes()
{
return m_FailedNodes.GetPointer();
}
const mitk::PropertyList *mitk::SceneIO::GetFailedProperties()
{
return m_FailedProperties;
}
void mitk::SceneIO::OnUnzipError(const void * /*pSender*/,
std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> &info)
{
++m_UnzipErrors;
MITK_ERROR << "Error while unzipping: " << info.second;
}
void mitk::SceneIO::OnUnzipOk(const void * /*pSender*/,
std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path> & /*info*/)
{
// MITK_INFO << "Unzipped ok: " << info.second.toString();
}
diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp
index 9337bdf315..82ef75b42d 100644
--- a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp
+++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp
@@ -1,468 +1,468 @@
/*============================================================================
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 "mitkSceneReaderV1.h"
#include "Poco/Path.h"
#include "mitkBaseRenderer.h"
#include "mitkIOUtil.h"
#include "mitkProgressBar.h"
#include "mitkPropertyListDeserializer.h"
#include "mitkSerializerMacros.h"
#include <mitkUIDManipulator.h>
#include <mitkRenderingModeProperty.h>
#include <tinyxml2.h>
#include <filesystem>
#include <map>
MITK_REGISTER_SERIALIZER(SceneReaderV1)
namespace
{
typedef std::pair<mitk::DataNode::Pointer, std::list<std::string>> NodesAndParentsPair;
bool NodeSortByLayerIsLessThan(const NodesAndParentsPair &left, const NodesAndParentsPair &right)
{
if (left.first.IsNotNull() && right.first.IsNotNull())
{
int leftLayer;
int rightLayer;
if (left.first->GetIntProperty("layer", leftLayer) && right.first->GetIntProperty("layer", rightLayer))
{
return leftLayer < rightLayer;
}
else
{
// fall back to name sort
return left.first->GetName() < right.first->GetName();
}
}
// in all other cases, fall back to stupid pointer comparison
// this is not reasonable but at least answers the sorting
// question clearly
return left.first.GetPointer() < right.first.GetPointer();
}
// This is a workaround until we are able to save time-related information in an
// actual file format of surfaces.
void ApplyProportionalTimeGeometryProperties(mitk::BaseData* data)
{
auto* geometry = dynamic_cast<mitk::ProportionalTimeGeometry*>(data->GetTimeGeometry());
if (nullptr == geometry)
return;
auto properties = data->GetPropertyList();
float value = 0.0f;
if (properties->GetFloatProperty("ProportionalTimeGeometry.FirstTimePoint", value))
{
if (value == -std::numeric_limits<float>::infinity())
value = std::numeric_limits<float>::lowest();
geometry->SetFirstTimePoint(value);
}
if (properties->GetFloatProperty("ProportionalTimeGeometry.StepDuration", value))
geometry->SetStepDuration(value);
}
mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const std::filesystem::path& basePath)
{
if (propertiesElement == nullptr)
return nullptr;
std::filesystem::path path(propertiesElement->Attribute("file"));
if (path.empty())
return nullptr;
if (!basePath.empty())
path = basePath / path;
auto deserializer = mitk::PropertyListDeserializer::New();
deserializer->SetFilename(path.string());
deserializer->Deserialize();
return deserializer->GetOutput();
}
}
bool mitk::SceneReaderV1::LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage)
{
assert(storage);
bool error(false);
// TODO prepare to detect errors (such as cycles) from wrongly written or edited xml files
- // Get number of elements to initialze progress bar
+ // Get number of elements to initialize progress bar
// 1. if there is a <data type="..." file="..."> element,
// - construct a name for the appropriate serializer
// - try to instantiate this serializer via itk object factory
// - if serializer could be created, use it to read the file into a BaseData object
// - if successful, call the new node's SetData(..)
// create a node for the tag "data" and test if node was created
typedef std::vector<mitk::DataNode::Pointer> DataNodeVector;
DataNodeVector DataNodes;
unsigned int listSize = 0;
for (auto *element = document.FirstChildElement("node"); element != nullptr;
element = element->NextSiblingElement("node"))
{
++listSize;
}
ProgressBar::GetInstance()->AddStepsToDo(listSize * 2);
// Deserialize base data properties before reading the actual data to be
// able to provide them as read-only meta data to the data reader.
std::map<std::string, PropertyList::Pointer> baseDataPropertyLists;
for (auto *nodeElement = document.FirstChildElement("node"); nodeElement != nullptr;
nodeElement = nodeElement->NextSiblingElement("node"))
{
const auto *uid = nodeElement->Attribute("UID");
if (uid == nullptr)
continue;
auto *dataElement = nodeElement->FirstChildElement("data");
if (dataElement != nullptr)
{
auto properties = DeserializeProperties(dataElement->FirstChildElement("properties"), workingDirectory);
if (properties.IsNotNull())
baseDataPropertyLists[uid] = properties;
}
}
for (auto *element = document.FirstChildElement("node"); element != nullptr;
element = element->NextSiblingElement("node"))
{
mitk::PropertyList* properties = nullptr;
const auto *uid = element->Attribute("UID");
if (uid != nullptr)
{
auto iter = baseDataPropertyLists.find(uid);
if (iter != baseDataPropertyLists.end())
properties = iter->second;
}
const auto *dataElement = element->FirstChildElement("data");
auto dataNode = this->LoadBaseDataFromDataTag(dataElement, properties, workingDirectory, error);
if (dataNode.IsNull())
continue;
auto* baseData = dataNode->GetData();
if (baseData != nullptr && properties != nullptr)
{
baseData->SetPropertyList(properties);
ApplyProportionalTimeGeometryProperties(baseData);
}
DataNodes.push_back(dataNode);
ProgressBar::GetInstance()->Progress();
}
// iterate all nodes
// first level nodes should be <node> elements
auto nit = DataNodes.begin();
for (auto *element = document.FirstChildElement("node"); element != nullptr || nit != DataNodes.end();
element = element->NextSiblingElement("node"), ++nit)
{
mitk::DataNode::Pointer node = *nit;
// 1. check child nodes
const char *uida = element->Attribute("UID");
std::string uid("");
if (uida)
{
uid = uida;
m_NodeForID[uid] = node.GetPointer();
m_IDForNode[node.GetPointer()] = uid;
}
else
{
MITK_ERROR << "No UID found for current node. Node will have no parents.";
error = true;
}
// 2. if there are <properties> nodes,
// - instantiate the appropriate PropertyListDeSerializer
// - use them to construct PropertyList objects
// - add these properties to the node (if necessary, use renderwindow name)
bool success = DecorateNodeWithProperties(node, element, workingDirectory);
if (!success)
{
MITK_ERROR << "Could not load properties for node.";
error = true;
}
// remember node for later adding to DataStorage
m_OrderedNodePairs.push_back(std::make_pair(node, std::list<std::string>()));
// 3. if there are <source> elements, remember parent objects
for (auto *source = element->FirstChildElement("source"); source != nullptr;
source = source->NextSiblingElement("source"))
{
const char *sourceUID = source->Attribute("UID");
if (sourceUID)
{
m_OrderedNodePairs.back().second.push_back(std::string(sourceUID));
}
}
ProgressBar::GetInstance()->Progress();
} // end for all <node>
// sort our nodes by their "layer" property
// (to be inserted in that order)
m_OrderedNodePairs.sort(&NodeSortByLayerIsLessThan);
// remove all unknown parent UIDs
for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
++nodesIter)
{
for (auto parentsIter = nodesIter->second.begin();
parentsIter != nodesIter->second.end();)
{
if (m_NodeForID.find(*parentsIter) == m_NodeForID.end())
{
parentsIter = nodesIter->second.erase(parentsIter);
MITK_WARN << "Found a DataNode with unknown parents. Will add it to DataStorage without any parent objects.";
error = true;
}
else
{
++parentsIter;
}
}
}
// repeat the following loop ...
// ... for all created nodes
unsigned int lastMapSize(0);
while (lastMapSize !=
m_OrderedNodePairs
.size()) // this is to prevent infinite loops; each iteration must at least add one node to DataStorage
{
lastMapSize = m_OrderedNodePairs.size();
// iterate (layer) ordered nodes backwards
// we insert the highest layers first
for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
++nodesIter)
{
bool addThisNode(true);
// if any parent node is not yet in DataStorage, skip node for now and check later
for (auto parentsIter = nodesIter->second.begin();
parentsIter != nodesIter->second.end();
++parentsIter)
{
if (!storage->Exists(m_NodeForID[*parentsIter]))
{
addThisNode = false;
break;
}
}
if (addThisNode)
{
DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New();
for (auto parentsIter = nodesIter->second.begin();
parentsIter != nodesIter->second.end();
++parentsIter)
{
parents->push_back(m_NodeForID[*parentsIter]);
}
// if all parents are found in datastorage (or are unknown), add node to DataStorage
storage->Add(nodesIter->first, parents);
// remove this node from m_OrderedNodePairs
m_OrderedNodePairs.erase(nodesIter);
// break this for loop because iterators are probably invalid
break;
}
}
}
// All nodes that are still in m_OrderedNodePairs at this point are not part of a proper directed graph structure.
// We'll add such nodes without any parent information.
for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
++nodesIter)
{
storage->Add(nodesIter->first);
MITK_WARN << "Encountered node that is not part of a directed graph structure. Will be added to DataStorage "
"without parents.";
error = true;
}
return !error;
}
mitk::DataNode::Pointer mitk::SceneReaderV1::LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement,
const PropertyList *properties,
const std::string &workingDirectory,
bool &error)
{
DataNode::Pointer node;
if (dataElement)
{
const char *filename = dataElement->Attribute("file");
if (filename && strlen(filename) != 0)
{
try
{
auto baseData = IOUtil::Load(workingDirectory + Poco::Path::separator() + filename, properties);
node = DataNode::New();
node->SetData(baseData);
}
catch (std::exception &e)
{
MITK_ERROR << "Error during attempt to read '" << filename << "'. Exception says: " << e.what();
error = true;
}
if (node.IsNull())
{
MITK_ERROR << "Error during attempt to read '" << filename << "'. Factory returned nullptr object.";
error = true;
}
}
else
{
MITK_ERROR << "File attribute of data tag is empty!";
error = true;
}
const char* dataUID = dataElement->Attribute("UID");
if (!error && dataUID != nullptr)
{
UIDManipulator manip(node->GetData());
manip.SetUID(dataUID);
}
}
// in case there was no <data> element we create a new empty node (for appending a propertylist later)
if (node.IsNull())
{
node = DataNode::New();
}
return node;
}
void mitk::SceneReaderV1::ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList)
{
// Basically call propertyList.Clear(), but implement exceptions (see bug 19354)
BaseData *data = node.GetData();
PropertyList::Pointer propertiesToKeep = PropertyList::New();
if (dynamic_cast<Image *>(data))
{
/*
Older scene files (before changes of bug 17547) could contain
a RenderingMode property with value "LevelWindow_Color".
Since bug 17547 this value has been removed and replaced by
the default value LookupTable_LevelWindow_Color.
This new default value does only result in "black-to-white"
CT images (or others) if there is a corresponding lookup
table. Such a lookup table is provided as a default value
by the Image mapper. Since that value was never present in
older scene files, we do well in not removing the new
default value here. Otherwise the mapper would fall back
to another default which is all the colors of the rainbow :-(
*/
BaseProperty::Pointer lutProperty = propertyList.GetProperty("LookupTable");
propertiesToKeep->SetProperty("LookupTable", lutProperty);
/*
Older scene files (before changes of T14807) may contain
multi-component images without the "Image.Displayed Component"
property.
As the treatment as multi-component image and the corresponding
visualization options hinges on that property we should not delete
it, if it was added by the mapper.
This is a fix for the issue reported in T19919.
*/
BaseProperty::Pointer compProperty = propertyList.GetProperty("Image.Displayed Component");
if (compProperty.IsNotNull())
{
propertiesToKeep->SetProperty("Image.Displayed Component", compProperty);
}
}
propertyList.Clear();
propertyList.ConcatenatePropertyList(propertiesToKeep);
}
bool mitk::SceneReaderV1::DecorateNodeWithProperties(DataNode *node,
const tinyxml2::XMLElement *nodeElement,
const std::string &workingDirectory)
{
assert(node);
assert(nodeElement);
bool error(false);
for (auto *properties = nodeElement->FirstChildElement("properties"); properties != nullptr;
properties = properties->NextSiblingElement("properties"))
{
const char *propertiesfilea(properties->Attribute("file"));
std::string propertiesfile(propertiesfilea ? propertiesfilea : "");
const char *renderwindowa(properties->Attribute("renderwindow"));
std::string renderwindow(renderwindowa ? renderwindowa : "");
PropertyList::Pointer propertyList =
node->GetPropertyList(renderwindow); // DataNode implementation always returns a propertylist
ClearNodePropertyListWithExceptions(*node, *propertyList);
// use deserializer to construct new properties
PropertyListDeserializer::Pointer deserializer = PropertyListDeserializer::New();
deserializer->SetFilename(workingDirectory + Poco::Path::separator() + propertiesfile);
bool success = deserializer->Deserialize();
error |= !success;
PropertyList::Pointer readProperties = deserializer->GetOutput();
if (readProperties.IsNotNull())
{
propertyList->ConcatenatePropertyList(readProperties, true); // true = replace
}
else
{
MITK_ERROR << "Property list reader did not return a property list. This is an implementation error. Please tell "
"your developer.";
error = true;
}
}
return !error;
}
diff --git a/Modules/SceneSerialization/test/mitkDataStorageCompare.h b/Modules/SceneSerialization/test/mitkDataStorageCompare.h
index a2b9868cf5..6f374b61f8 100644
--- a/Modules/SceneSerialization/test/mitkDataStorageCompare.h
+++ b/Modules/SceneSerialization/test/mitkDataStorageCompare.h
@@ -1,245 +1,245 @@
/*============================================================================
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 mitkDataStorageCompare_h
#define mitkDataStorageCompare_h
#include "mitkDataStorage.h"
namespace mitk
{
/**
Helper class to compare two DataStorages.
This is purposely _not_ a mitk::Equal() function because
the main intention is to use this class for I/O testing,
and because the definition of equal is really not clear
for two DataStorages.
In the context of I/O tests, one could want to verify
(given two storages "reference" and "test")
* that all nodes from reference are in test (identification of nodes is difficult)
* all nodes from test are in reference
* all property lists are identical (types and values)
* all data is identical (types, values)
* all mappers are identical (types, not state)
* all interactors are identical (type)
* ... ?
This class offers a number of flags that describe one of
those aspects. These flags can be combined by the OR
operator ("|"). The Compare() method will only test those
aspects.
Once Compare() has been executed there are a couple of
methods to query the results or print a small report.
<b>Finding nodes</b>:
Identifying nodes to be compares poses a problem. This is
because we don't have node identifiers but only names.
Names are not unique and are not even required to exist
or to be non-empty.
This class does not solve this problem completely. It
identifies nodes by constructing an "identifier" that
includes the node name and the BaseData class name of
the node itself and all its direct and indirect parents.
This leaves certain cases where two nodes in the hierarchy
would get the same identifier. For the use in test cases
(\sa SceneIOTestScenarioProvider) this is sufficient, however,
since we can make sure that each node gets an individual name.
*/
class DataStorageCompare
{
public:
/**
* \brief Flag describing the aspects of comparing two DataStorages.
*
* Flag values can be combined by the "|" operator (bitwise or).
*/
typedef enum {
CMP_Nothing = 0, ///< No tests
CMP_Hierarchy = 1, ///< Compare hierarchy
CMP_Data = 2, ///< Compare BaseData
CMP_Properties = 4, ///< Compare PropertyLists
CMP_Mappers = 8, ///< Compare Mapper types
CMP_Interactors = 16, ///< Compare interactors
CMP_All = 0xfffffff, ///< Compare all known aspects
} Tests;
/**
* \brief Constructor taking reference and test DataStorage.
*
* \param reference Reference DataStorage object.
* \param test Test DataStorage object.
* \param flags Flags describing the desired test aspects (\sa Tests).
* \param eps precision for floating point comparisons.
*/
DataStorageCompare(const DataStorage *reference,
const DataStorage *test,
Tests flags = CMP_All,
double eps = mitk::eps);
/**
* \brief Shorthand for Compare(true).
*/
bool CompareVerbose();
/**
* \brief Execute the comparison.
*
* \param verbose if true, the comparison will output messages for all differences found and Report() will be called
* at the end.
* \return true if reference and test are judged equal regarding all flags provided in constructor.
*/
bool Compare(bool verbose = false);
/**
* \brief Prints a small summary of what tests have been executed and which ones failed or passed.
*
* Called automatically by Compare() when that method is called with the verbose flag.
*/
void Report();
private:
/**
* \brief Compares that the structure of nodes is identical.
*
* See class comment on identifying nodes for details.
*
* Tests both directions: test nodes included in reference
* storage and reference nodes included in test storage.
*/
bool CompareHierarchy(bool verbose);
/**
* \brief Compares pairs of DataNodes to each other.
*
* See class comment on identifying nodes for details.
*
* Identifies nodes pairs and calls AreNodesEqual() for
* pairs that are clearly identified. Warnings are issues
* for unclear cases.
*/
bool CompareDataNodes(bool verbose);
/**
* \brief Called for each pair of nodes, tests all aspects asked for in constructor.
*
* Receives a reference and a test DataNode and executes all comparisons
* that where required during construction of this DataStorageCompare
* via the Tests flags.
*
* Calls to specific methods for each type of comparison.
*
* \sa IsDataEqual()
* \sa ArePropertyListsEqual()
* \sa AreMappersEqual()
*/
bool AreNodesEqual(const mitk::DataNode *reference, const mitk::DataNode *test, bool verbose = false);
/**
* \brief Compares two BaseData instances.
*
* Makes use of the BaseDataEqual services to compare objects.
*/
bool IsDataEqual(const mitk::BaseData *reference, const mitk::BaseData *test, bool verbose = false);
/**
* \brief Compares all property lists of given nodes.
*
* Tests presence of all render specific and default lists,
* then compares them via the other method named ArePropertyListsEqual().
*/
bool ArePropertyListsEqual(const mitk::DataNode &reference, const mitk::DataNode &test, bool verbose = false);
/**
* \brief Compares the mapper types(!) of given nodes.
*
*/
bool AreMappersEqual(const mitk::DataNode &reference, const mitk::DataNode &test, bool verbose);
/**
* \brief Compares two instances of PropertyList.
*
* Tests if all properties are found in both lists.
* Tests equalness of properties via BaseProperty::operator==().
*/
bool ArePropertyListsEqual(const mitk::PropertyList &reference,
const mitk::PropertyList &test,
bool verbose = false);
/// Precision/Epsilon used for certain floating point comparisons.
double m_Eps;
/// Flags describing what to compare.
Tests m_TestAspects;
/// Reference data storage.
DataStorage::ConstPointer m_ReferenceDS;
/// Test data storage.
DataStorage::ConstPointer m_TestDS;
/**
- * \brief Structure associating "hierachy descriptors" to DataNodes.
+ * \brief Structure associating "hierarchy descriptors" to DataNodes.
*
* The string keys are "hierarchy descriptors" which are
* built by GenerateHierarchyDescriptor() for individual nodes.
*/
typedef std::multimap<std::string, DataNode::Pointer> HierarchyDescriptorMap;
/// Structure of the reference storage, filled by Compare().
HierarchyDescriptorMap m_RefNodesByHierarchy;
/// Structure of the test storage, filled by Compare().
HierarchyDescriptorMap m_TestNodesByHierarchy;
/**
- * \brief Calculate hierachy descriptors for each node, store them in the result map.
+ * \brief Calculate hierarchy descriptors for each node, store them in the result map.
*
*/
void DescribeHierarchyOfNodes(DataStorage::ConstPointer storage, HierarchyDescriptorMap &result);
/**
* \brief Generate descriptor for one single node.
*
*/
std::string GenerateNodeDescriptor(mitk::DataNode::Pointer node);
/**
* \brief Generate descriptor for the complete hierarchy of a node.
*
* \param node DataNode to describe.
* \param storage DataStorage to find out about the node's parents.
*/
std::string GenerateHierarchyDescriptor(DataNode::Pointer node, DataStorage::ConstPointer storage);
bool m_HierarchyPassed;
bool m_DataPassed;
bool m_PropertiesPassed;
bool m_MappersPassed;
bool m_InteractorsPassed;
int m_AspectsFailed;
}; // class
/// Needed to make the flag DataStorageCompare::Tests usable.
inline DataStorageCompare::Tests operator|(DataStorageCompare::Tests a, DataStorageCompare::Tests b)
{
return static_cast<DataStorageCompare::Tests>(static_cast<int>(a) | static_cast<int>(b));
}
} // namespace
#endif
diff --git a/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h b/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h
index 7db8477d82..017b6416c5 100644
--- a/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h
+++ b/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h
@@ -1,214 +1,214 @@
/*============================================================================
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 mitkSceneIOTestScenarioProvider_h
#define mitkSceneIOTestScenarioProvider_h
#include "mitkStandaloneDataStorage.h"
namespace mitk
{
/**
\brief Provides DataStorages that serve as test scenarios.
This class provides the structure Scenario to describe a
single test scenario for SceneIO and similar objects.
A single scenario consists of
- a DataStorage (in reality a method that constructs one)
- an identifier / name
- some flags that describe the scenario
\warning Probably we should split the scenario and
its description (== our expectations) at some
time. This will be necessary once that we have
two SceneIO mechanisms that differ in their
capabilities.
*/
class SceneIOTestScenarioProvider
{
public:
/// Typedef for type BuilderMethodPointer which is function pointer to member of SceneIOTestScenarioProvider
typedef DataStorage::Pointer (SceneIOTestScenarioProvider::*BuilderMethodPointer)() const;
/**
Structure to describe a single scenario.
Holds some descriptive members plus a pointer to a
method in SceneIOTestScenarioProvider that is able
to create a DataStorage. This DataStorage shall
represent some particularity to be tested in a
related test.
*/
struct Scenario
{
std::string key; ///< Description / ID.
bool serializable; ///< Do we expect that this can be stored in a .mitk file?
std::string referenceArchiveFilename; ///< Absolute filename with a reference .mitk file.
bool referenceArchiveLoadable; ///< Do we expect that the reference can be loaded without errors?
double comparisonPrecision; ///< Precision used for floating point comparisons after save/load cycle (eps).
/// Construct the DataStorage for this scenario.
DataStorage::Pointer BuildDataStorage() const;
/**
\param _key Description / ID.
\param _scenarioProvider object that contains the member function in _providerMethod
\param _providerMethod pointer to a member that creates a DataStorage for the scenario
\param _isSerializable Do we expect that this can be stored in a .mitk file?
\param _referenceArchiveFilename Absolute filename with a reference .mitk file.
\param _isReferenceLoadable Do we expect that the reference can be loaded without errors?
- \param _comparisonPrecision Precision used for floating point comparisions after save/load cycle (eps).
+ \param _comparisonPrecision Precision used for floating point comparisons after save/load cycle (eps).
*/
Scenario(const std::string &_key,
const SceneIOTestScenarioProvider *_scenarioProvider,
SceneIOTestScenarioProvider::BuilderMethodPointer _providerMethod,
bool _isSerializable,
const std::string &_referenceArchiveFilename,
bool _isReferenceLoadable,
double _comparisonPrecision);
private:
const SceneIOTestScenarioProvider *m_ScenarioProvider;
SceneIOTestScenarioProvider::BuilderMethodPointer m_ProviderMethod;
};
/// List of Scenarios.
typedef std::vector<Scenario> ScenarioList;
/// Returns _all_ registered scenarios.
ScenarioList GetAllScenarios() const;
private:
ScenarioList m_Scenarios;
/// Configures how many items count as many for some tests.
int m_HowMuchIsMany;
/**
Registers one scenario with its description and a method for DataStorage creations.
*/
void AddScenario(const std::string &key,
BuilderMethodPointer creator,
bool isSerializable,
const std::string &referenceArchiveFilename = std::string(),
bool isReferenceLoadable = false,
double comparisionPrecision = mitk::eps);
/**
An empty storage.
*/
DataStorage::Pointer EmptyStorage() const;
/**
One single node without anything.
*/
DataStorage::Pointer OneEmptyNode() const;
/**
One single node with a name.
*/
DataStorage::Pointer OneEmptyNamedNode() const;
/**
Flat list.
\verbatim
|-o
|-o
|-o
|-o
...
\endverbatim
*/
DataStorage::Pointer ManyTopLevelNodes() const;
/**
Degenerated tree.
A tree like this:
\verbatim
|-o
|-o
|-o
...
\endverbatim
*/
DataStorage::Pointer LineOfManyOnlyChildren() const;
/**
Nodes with multiple parents.
*/
DataStorage::Pointer ComplicatedFamilySituation() const;
/**
Basic core type Image.
*/
DataStorage::Pointer Image() const;
/**
Basic core type Surface.
*/
DataStorage::Pointer Surface() const;
/**
Basic core type PointSet.
*/
DataStorage::Pointer PointSet() const;
/**
GeometryData object (separate for specific precision).
*/
DataStorage::Pointer GeometryData() const;
/**
Properties for various render windows, containing no or long names or values.
*/
DataStorage::Pointer SpecialProperties() const;
public:
// Helper to simplify writing the registration
#define AddSaveAndRestoreScenario(name) AddScenario(#name, &mitk::SceneIOTestScenarioProvider::name, true);
// this one in the header so it is clearly visible when someone
// adds a test to the bottom of the list
SceneIOTestScenarioProvider() : m_HowMuchIsMany(50)
{
/// declare all your test cases here!
AddSaveAndRestoreScenario(EmptyStorage);
AddSaveAndRestoreScenario(OneEmptyNode);
AddSaveAndRestoreScenario(OneEmptyNamedNode);
AddSaveAndRestoreScenario(ManyTopLevelNodes);
AddSaveAndRestoreScenario(LineOfManyOnlyChildren);
AddSaveAndRestoreScenario(ComplicatedFamilySituation);
AddSaveAndRestoreScenario(Image);
AddSaveAndRestoreScenario(Surface);
AddSaveAndRestoreScenario(PointSet);
if (sizeof(size_t) != 4)
// this test is deactivated on 32 bit systems since it fails
// on the only 32 bit dartclient. To activate it there, one
// would have to debug the precision problem on a 32 bit
// machine and either adapt expectations or fix a bug.
AddSaveAndRestoreScenario(GeometryData);
AddSaveAndRestoreScenario(SpecialProperties);
// AddScenario("GeometryData", &mitk::SceneIOTestScenarioProvider::GeometryData, true, std::string(), false,
// mitk::eps);
}
}; // class
} // namespace
#endif
diff --git a/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h b/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h
index 0ac6ef7e12..736e20bd9d 100644
--- a/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h
+++ b/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h
@@ -1,149 +1,149 @@
/*============================================================================
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 mitkVectorPropertySerializer_h
#define mitkVectorPropertySerializer_h
#include "mitkBasePropertySerializer.h"
#include "mitkVectorProperty.h"
#include <mitkLexicalCast.h>
#include <tinyxml2.h>
namespace mitk
{
/**
\brief Serializes a VectorProperty
Serializes an instance of VectorProperty into a XML structure like
\verbatim
<Values>
<Value idx="0" value="17.3"/>
<Value idx="1" value="7.2"/>
<Value idx="2" value="-17.3"/>
</Values>
\endverbatim
This class is implemented as a template and makes use of std::stringstream
for necessary conversions of specific data types to and from string.
For numeric types, the class adds a precision token to stringstream that
should usually suffice.
*/
template <typename DATATYPE>
class MITKSCENESERIALIZATIONBASE_EXPORT VectorPropertySerializer : public BasePropertySerializer
{
public:
// Expand manually most of mitkClassMacro:
// mitkClassMacro(VectorProperty<DATATYPE>, mitk::BaseProperty);
- // This manual expansion is done to override explicitely
+ // This manual expansion is done to override explicitly
// the GetNameOfClass methods
typedef VectorProperty<DATATYPE> PropertyType;
typedef VectorPropertySerializer<DATATYPE> Self;
typedef BasePropertySerializer SuperClass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
std::vector<std::string> GetClassHierarchy() const override { return mitk::GetClassHierarchy<Self>(); }
// This function must return different
// strings in function of the template parameter!
// Serialization depends on this feature.
static const char *GetStaticNameOfClass()
{
// concatenate a prefix dependent on the template type and our own classname
static std::string nameOfClass =
std::string(VectorPropertyDataType<DATATYPE>::prefix()) + "VectorPropertySerializer";
return nameOfClass.c_str();
}
const char *GetNameOfClass() const override { return this->GetStaticNameOfClass(); }
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
//! Build an XML version of this property
tinyxml2::XMLElement* Serialize(tinyxml2::XMLDocument& doc) override
{
auto *listElement = doc.NewElement("Values");
if (const PropertyType *prop = dynamic_cast<const PropertyType *>(m_Property.GetPointer()))
{
typename PropertyType::VectorType elements = prop->GetValue();
unsigned int index(0);
for (auto listEntry : elements)
{
std::stringstream indexS;
indexS << index++;
auto *entryElement = doc.NewElement("Value");
entryElement->SetAttribute("idx", indexS.str().c_str());
entryElement->SetAttribute("value", boost::lexical_cast<std::string>(listEntry).c_str());
listElement->InsertEndChild(entryElement);
}
return listElement;
}
else
{
return nullptr;
}
}
//! Construct a property from an XML serialization
BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *listElement) override
{
typename PropertyType::VectorType datalist;
if (listElement)
{
std::string valueString;
DATATYPE value;
for (auto *valueElement = listElement->FirstChildElement("Value"); valueElement;
valueElement = valueElement->NextSiblingElement("Value"))
{
valueString = valueElement->Attribute("value");
if (valueString.empty())
{
MITK_ERROR << "Missing value attribute in <Values> list";
return nullptr;
}
try
{
value = boost::lexical_cast<DATATYPE>(valueString);
}
catch (boost::bad_lexical_cast &e)
{
MITK_ERROR << "Could not parse '" << valueString << "' as number: " << e.what();
return nullptr;
}
datalist.push_back(value);
}
typename PropertyType::Pointer property = PropertyType::New();
property->SetValue(datalist);
return property.GetPointer();
}
else
{
MITK_ERROR << "Missing <Values> tag.";
}
return nullptr;
}
};
typedef VectorPropertySerializer<double> DoubleVectorPropertySerializer;
typedef VectorPropertySerializer<int> IntVectorPropertySerializer;
} // namespace
#endif
diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
index aeb755b305..6e4ef33a4e 100644
--- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
+++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
@@ -1,422 +1,422 @@
/*============================================================================
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 _itkAdaptiveThresholdIterator_txx
#define _itkAdaptiveThresholdIterator_txx
#include "itkAdaptiveThresholdIterator.h"
#include "mitkProgressBar.h"
#include <cmath>
namespace itk
{
template <class TImage, class TFunction>
AdaptiveThresholdIterator<TImage, TFunction>::AdaptiveThresholdIterator(ImageType *imagePtr,
FunctionType *fnPtr,
IndexType startIndex)
: m_FineDetectionMode(false), m_DetectionStop(false)
{
this->m_OutputImage = imagePtr;
m_Function = fnPtr;
m_StartIndices.push_back(startIndex);
// Set up the temporary image
this->InitializeIterator();
}
template <class TImage, class TFunction>
AdaptiveThresholdIterator<TImage, TFunction>::AdaptiveThresholdIterator(ImageType *imagePtr,
FunctionType *fnPtr,
std::vector<IndexType> &startIndex)
: m_FineDetectionMode(false), m_DetectionStop(false)
{
this->m_OutputImage = imagePtr;
m_Function = fnPtr;
unsigned int i;
for (i = 0; i < startIndex.size(); i++)
{
m_StartIndices.push_back(startIndex[i]);
}
// Set up the temporary image
this->InitializeIterator();
}
template <class TImage, class TFunction>
AdaptiveThresholdIterator<TImage, TFunction>::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr)
: m_FineDetectionMode(false), m_DetectionStop(false)
{
- this->m_OutputImage = imagePtr; // here we store the image, we have to wite the result to
+ this->m_OutputImage = imagePtr; // here we store the image, we have to write the result to
m_Function = fnPtr;
// Set up the temporary image
this->InitializeIterator();
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::InitializeIterator()
{
// Get the origin and spacing from the image in simple arrays
m_ImageOrigin = this->m_OutputImage->GetOrigin();
m_ImageSpacing = this->m_OutputImage->GetSpacing();
m_ImageRegion = this->m_OutputImage->GetBufferedRegion();
this->InitRegionGrowingState();
m_VoxelCounter = 0;
m_LastVoxelNumber = 0;
m_CurrentLeakageRatio = 0;
m_DetectedLeakagePoint = 0;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::SetExpansionDirection(bool upwards)
{
m_UpwardsExpansion = upwards;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::IncrementRegionGrowingState()
{
// make the progressbar go one step further
if (!m_FineDetectionMode)
mitk::ProgressBar::GetInstance()->Progress();
// updating the thresholds
if (m_UpwardsExpansion)
{
this->ExpandThresholdUpwards();
}
else
{
this->ExpandThresholdDownwards();
}
// leakage-detection
int criticalValue = 2000; // calculate a bit more "intelligent"
if (!m_FineDetectionMode)
{
int diff = m_VoxelCounter - m_LastVoxelNumber;
if (diff > m_CurrentLeakageRatio)
{
m_CurrentLeakageRatio = diff;
m_DetectedLeakagePoint = m_RegionGrowingState;
}
m_LastVoxelNumber = m_VoxelCounter;
m_VoxelCounter = 0;
}
else // fine leakage detection
{
- // counting voxels over interations; if above a critical value (to be extended) then set this to leakage
+ // counting voxels over iterations; if above a critical value (to be extended) then set this to leakage
int diff = m_VoxelCounter - m_LastVoxelNumber;
// std::cout<<"diff is: "<<diff<<"\n";
if (diff <= criticalValue && (!m_DetectionStop))
{
// m_CurrentLeakageRatio = diff;
m_DetectedLeakagePoint = m_RegionGrowingState + 1; // TODO check why this is needed
// std::cout<<"setting CurrentLeakageRatio to: "<<diff<<" and leakagePoint to: "<<m_DetectedLeakagePoint<<"\n";
}
else
{
m_DetectionStop = true;
// std::cout<<"\n\n[ITERATOR] detection stop!!!\n";
}
m_LastVoxelNumber = m_VoxelCounter;
m_VoxelCounter = 0;
}
// increment the counter
m_RegionGrowingState++;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::ExpandThresholdUpwards()
{
int upper = (int)m_Function->GetUpper();
upper++;
m_Function->ThresholdBetween(m_MinTH, upper);
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::ExpandThresholdDownwards()
{
int lower = (int)m_Function->GetLower();
lower--;
m_Function->ThresholdBetween(lower, m_MaxTH);
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::InitRegionGrowingState()
{
this->m_RegionGrowingState = 1;
}
template <class TImage, class TFunction>
int AdaptiveThresholdIterator<TImage, TFunction>::EstimateDistance(IndexType tempIndex)
{
PixelType value = this->m_Function->GetInputImage()->GetPixel(tempIndex);
PixelType minPixel = PixelType(m_MinTH);
PixelType maxPixel = PixelType(m_MaxTH);
if (value > maxPixel || value < minPixel)
{
return 0;
}
if (m_UpwardsExpansion)
{
return (int)(value - m_SeedPointValue);
}
else
{
return (int)(m_SeedPointValue - value);
}
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::SetMinTH(int min)
{
m_MinTH = min;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::SetMaxTH(int max)
{
m_MaxTH = max;
}
template <class TImage, class TFunction>
int AdaptiveThresholdIterator<TImage, TFunction>::GetSeedPointValue()
{
return this->m_SeedPointValue;
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::GoToBegin()
{
m_QueueMap.clear();
m_SeedPointValue = 0;
IndexType seedIndex = m_StartIndices[0];
bool doAverage = false; // enable or disable manually!
if (doAverage)
{
// loops for creating the sum of the N27-neighborhood around the seedpoint
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
for (int k = -1; k <= 1; k++)
{
seedIndex[0] = seedIndex[0] + i;
seedIndex[1] = seedIndex[1] + j;
seedIndex[2] = seedIndex[2] + k;
m_SeedPointValue += (int)m_Function->GetInputImage()->GetPixel(seedIndex);
}
}
}
// value of seedpoint computed from mean of N27-neighborhood
m_SeedPointValue = m_SeedPointValue / 27;
}
else
{
m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(seedIndex);
}
this->CheckSeedPointValue();
m_InitializeValue = (this->CalculateMaxRGS() + 1);
if (!m_FineDetectionMode)
mitk::ProgressBar::GetInstance()->AddStepsToDo(m_InitializeValue - 1);
// only initialize with zeros for the first segmention (raw segmentation mode)
if (!m_FineDetectionMode)
{
this->m_OutputImage->FillBuffer((PixelType)0);
}
if (m_UpwardsExpansion)
{
m_Function->ThresholdBetween(m_MinTH, m_SeedPointValue);
}
else
{
m_Function->ThresholdBetween(m_SeedPointValue, m_MaxTH);
}
this->m_IsAtEnd = true;
seedIndex = m_StartIndices[0]; // warum noch mal? Steht doch schon in Zeile 224
if (this->m_OutputImage->GetBufferedRegion().IsInside(seedIndex) && this->m_SeedPointValue >= this->m_MinTH &&
this->m_SeedPointValue <= this->m_MaxTH)
{
// Push the seed onto the queue
this->InsertIndexTypeIntoQueueMap(m_RegionGrowingState, seedIndex);
// Obviously, we're at the beginning
this->m_IsAtEnd = false;
this->m_OutputImage->SetPixel(seedIndex, (m_InitializeValue - m_RegionGrowingState));
}
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::CheckSeedPointValue()
{
// checks, if the value, that has been averaged over the N-27 neighborhood aorund the seedpoint is still inside
// the thresholds-ranges. if not, the actual value of the seedpoint (not averaged) is used
if (m_SeedPointValue < m_MinTH || m_SeedPointValue > m_MaxTH)
{
m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(m_StartIndices[0]);
}
}
template <class TImage, class TFunction>
unsigned int AdaptiveThresholdIterator<TImage, TFunction>::CalculateMaxRGS()
{
if (m_UpwardsExpansion)
{
return (m_MaxTH - m_SeedPointValue);
}
else
{
return (m_SeedPointValue - m_MinTH);
}
}
template <class TImage, class TFunction>
bool AdaptiveThresholdIterator<TImage, TFunction>::IsPixelIncluded(const IndexType &index) const
{
// checks, if grayvalue of current voxel is inside the currently used thresholds
return this->m_Function->EvaluateAtIndex(index);
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index)
{
// first check if the key-specific queue already exists
if (m_QueueMap.count(key) == 0)
{
// if queue doesn´t exist, create it, push the IndexType onto it
// and insert it into the map
IndexQueueType newQueue;
newQueue.push(index);
typedef typename QueueMapType::value_type KeyIndexQueue;
m_QueueMap.insert(KeyIndexQueue(key, newQueue));
}
else
{
// if queue already exists in the map, push IndexType onto its specific queue
(*(m_QueueMap.find(key))).second.push(index);
}
}
template <class TImage, class TFunction>
void AdaptiveThresholdIterator<TImage, TFunction>::DoExtendedFloodStep()
{
// The index in the front of the queue should always be
// valid and be inside since this is what the iterator
// uses in the Set/Get methods. This is ensured by the
// GoToBegin() method.
typename QueueMapType::iterator currentIt = m_QueueMap.find(m_RegionGrowingState);
if (currentIt == m_QueueMap.end())
{
this->IncrementRegionGrowingState();
}
else
{
IndexQueueType *currentQueue = &(*currentIt).second;
// Take the index in the front of the queue
const IndexType &topIndex = currentQueue->front();
// Iterate through all possible dimensions
// NOTE: Replace this with a ShapeNeighborhoodIterator
for (unsigned int i = 0; i < NDimensions; i++)
{
// The j loop establishes either left or right neighbor (+-1)
for (int j = -1; j <= 1; j += 2)
{
IndexType tempIndex;
// build the index of a neighbor
for (unsigned int k = 0; k < NDimensions; k++)
{
if (i != k)
{
tempIndex[k] = topIndex[k];
}
else
{
tempIndex[k] = topIndex[k] + j;
}
} // end build the index of a neighbor
// If this is a valid index and have not been tested,
// then test it.
if (m_ImageRegion.IsInside(tempIndex))
{
// check if voxel hasn´t already been processed
if (this->m_OutputImage->GetPixel(tempIndex) == 0)
{
// if it is inside, push it into the queue
if (this->IsPixelIncluded(tempIndex))
{
// hier wird Voxel in momentan aktiven Stack und ins OutputImage geschrieben
this->InsertIndexTypeIntoQueueMap((m_RegionGrowingState), tempIndex);
this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - m_RegionGrowingState));
}
else // If the pixel is not inside the current threshold
{
int distance = this->EstimateDistance(
tempIndex); // [!] sollte nicht estimateDistance sondern calculateDistance() heißen!
if (distance != 0)
{
// hier wird Voxel in entsprechenden Stack und ins OutputImage geschrieben
this->InsertIndexTypeIntoQueueMap((distance), tempIndex);
this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - distance));
}
}
}
}
} // end left/right neighbor loop
} // end check all neighbors
// Now that all the potential neighbors have been
// inserted we can get rid of the pixel in the front
currentQueue->pop();
m_VoxelCounter++;
if (currentQueue->empty())
{
// if currently used queue is empty
this->IncrementRegionGrowingState();
}
}
if (m_RegionGrowingState >= (m_InitializeValue) || m_DetectionStop)
{
this->m_IsAtEnd = true;
// std::cout << "RegionGrowing finished !" << std::endl;
// std::cout << "Detected point of leakage: " << m_DetectedLeakagePoint << std::endl;
}
}
} // end namespace itk
#endif
diff --git a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx
index c6e07c296d..b0cfd8abc0 100644
--- a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx
+++ b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx
@@ -1,305 +1,305 @@
/*============================================================================
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 _itkConnectedAdaptiveThresholdImageFilter_txx
#define _itkConnectedAdaptiveThresholdImageFilter_txx
#include "itkAdaptiveThresholdIterator.h"
#include "itkBinaryThresholdImageFunction.h"
#include "itkConnectedAdaptiveThresholdImageFilter.h"
#include "itkMinimumMaximumImageFilter.h"
#include "itkThresholdImageFilter.h"
namespace itk
{
/**
* Constructor
*/
template <class TInputImage, class TOutputImage>
ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::ConnectedAdaptiveThresholdImageFilter()
: m_OutoutImageMaskFineSegmentation(nullptr),
m_GrowingDirectionIsUpwards(true),
m_SeedpointValue(0),
m_DetectedLeakagePoint(0),
m_InitValue(0),
m_AdjLowerTh(0),
m_AdjUpperTh(0),
m_FineDetectionMode(false),
m_DiscardLastPreview(false),
m_SegmentationCancelled(false)
{
}
template <class TInputImage, class TOutputImage>
void ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::GenerateData()
{
typename ConnectedAdaptiveThresholdImageFilter::InputImageConstPointer inputImage = this->GetInput();
typename ConnectedAdaptiveThresholdImageFilter::OutputImagePointer outputImage = this->GetOutput();
typename Superclass::InputPixelObjectType::Pointer lowerThreshold = this->GetLowerInput();
typename Superclass::InputPixelObjectType::Pointer upperThreshold = this->GetUpperInput();
// kommt drauf, wie wir hier die Pipeline aufbauen
this->SetLower(lowerThreshold->Get());
this->SetUpper(upperThreshold->Get());
typedef BinaryThresholdImageFunction<InputImageType> FunctionType;
typedef AdaptiveThresholdIterator<OutputImageType, FunctionType> IteratorType;
int initValue = IteratorType::CalculateInitializeValue((int)(this->GetLower()), (int)(this->GetUpper()));
// Initialize the output according to the segmentation (fine or raw)
if (m_FineDetectionMode)
{
outputImage = this->m_OutoutImageMaskFineSegmentation;
}
typename ConnectedAdaptiveThresholdImageFilter::OutputImageRegionType region = outputImage->GetRequestedRegion();
outputImage->SetBufferedRegion(region);
outputImage->Allocate();
if (!m_FineDetectionMode)
- { // only initalize the output image if we are using the raw segmentation mode
+ { // only initialize the output image if we are using the raw segmentation mode
outputImage->FillBuffer((typename ConnectedAdaptiveThresholdImageFilter::OutputImagePixelType)initValue);
}
typename FunctionType::Pointer function = FunctionType::New();
function->SetInputImage(inputImage);
typename Superclass::SeedContainerType seeds;
seeds = this->GetSeeds();
// pass parameters needed for region growing to iterator
IteratorType it(outputImage, function, seeds);
it.SetFineDetectionMode(m_FineDetectionMode);
it.SetExpansionDirection(m_GrowingDirectionIsUpwards);
it.SetMinTH((int)(this->GetLower()));
it.SetMaxTH((int)(this->GetUpper()));
it.GoToBegin();
this->m_SeedpointValue = it.GetSeedPointValue();
if ((this->GetLower()) > this->m_SeedpointValue || this->m_SeedpointValue > (this->GetUpper()))
{
// set m_SegmentationCancelled to true, so if it doesn't reach the point where it is set back to false
- // we can asssume that there was an error
+ // we can assume that there was an error
this->m_SegmentationCancelled = true;
return;
}
// iterate through image until
while (!it.IsAtEnd())
{
// make iterator go one step further (calls method DoFloodStep())
++it;
}
this->m_DetectedLeakagePoint = it.GetLeakagePoint();
this->m_SegmentationCancelled = false;
}
template <class TInputImage, class TOutputImage>
TOutputImage *itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::GetResultImage()
{
return m_OutoutImageMaskFineSegmentation;
}
template <class TInputImage, class TOutputImage>
typename ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::IndexType
itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::CorrectSeedPointPosition(
unsigned int sizeOfVolume, int lowerTh, int upperTh)
{
typename ConnectedAdaptiveThresholdImageFilter::InputImageConstPointer inputImage = this->GetInput();
typedef typename TInputImage::IndexType IndexType;
IndexType itkIntelligentSeedIndex;
int seedPixelValue = inputImage->GetPixel(m_SeedPointIndex);
// set new seed index to the voxel with the darkest value and shortest distance to original seed
if (seedPixelValue > upperTh || seedPixelValue < lowerTh)
{
// MITK_INFO << "seed pixel value [BEFORE] = " << seedPixelValue;
// ToDo crop region
itk::Index<3> workindex;
for (int i = 0; i < 3; i++)
{
workindex[i] = m_SeedPointIndex[i] - sizeOfVolume / 2;
if (workindex[i] < 0)
workindex[i] = 0;
}
itk::Size<3> worksize;
for (int i = 0; i < 3; i++)
{
worksize[i] = sizeOfVolume;
}
itk::ImageRegion<3> workregion(workindex, worksize);
itk::ImageRegionIterator<TInputImage> regionIt(const_cast<TInputImage *>(inputImage.GetPointer()), workregion);
// int darkestGrayValue=seedPixelValue;
int currentGrayValue;
float distance = (float)(sizeOfVolume / 2);
float relativeDistance = 1; // between 0 and 1
mitk::Vector3D seedVector, currentVector;
mitk::FillVector3D(seedVector, m_SeedPointIndex[0], m_SeedPointIndex[1], m_SeedPointIndex[2]);
currentVector = seedVector;
float costValue = 0; // beware, Depending on seeking upper or lower value...
for (regionIt.GoToBegin(); !regionIt.IsAtEnd(); ++regionIt)
{
// get current gray value
currentGrayValue = regionIt.Value();
// get current seed index
m_SeedPointIndex = regionIt.GetIndex();
// fill current vector
mitk::FillVector3D(currentVector, m_SeedPointIndex[0], m_SeedPointIndex[1], m_SeedPointIndex[2]);
// calculate distance from original seed to new seed
mitk::Vector3D distVector = currentVector - seedVector;
distance = fabs(distVector.GetSquaredNorm());
relativeDistance = distance / (sizeOfVolume / 2);
// calculate "cost function"
float currentCostValue = (1 - relativeDistance) * currentGrayValue;
if (currentCostValue < costValue && currentGrayValue < upperTh)
{
itkIntelligentSeedIndex = regionIt.GetIndex();
costValue = currentCostValue;
// MITK_INFO <<"cost value="<< costValue;
// MITK_INFO <<"darkest and closest Voxel ="<< currentGrayValue;
// MITK_INFO <<"m_UPPER="<< upperTh;
}
}
// MITK_INFO<< "seed pixel value [AFTER] =" << inputImage->GetPixel(itkIntelligentSeedIndex) <<"\n";
}
else
{ // no correction of the seed point is needed, just pass the original seed
itkIntelligentSeedIndex = m_SeedPointIndex;
}
return itkIntelligentSeedIndex;
}
template <class TInputImage, class TOutputImage>
void itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::CropMask(unsigned int croppingSize)
{
// initialize center point of the working region
itk::Index<3> workindex;
for (int i = 0; i < 3; i++)
{
workindex[i] = m_SeedPointIndex[i] - croppingSize / 2;
if (workindex[i] < 0)
workindex[i] = 0;
}
// initialize working volume
itk::Size<3> worksize;
for (int i = 0; i < 3; i++)
{
worksize[i] = croppingSize;
}
// set working region
itk::ImageRegion<3> workregion(workindex, worksize);
// check if the entire region is inside the image
if (!(m_OutoutImageMaskFineSegmentation->GetLargestPossibleRegion().IsInside(workregion)))
{
// if not then crop to the intersection of the image (gemeinsame Schnittmenge Bild und workingRegion)
if (!(workregion.Crop(m_OutoutImageMaskFineSegmentation->GetLargestPossibleRegion())))
{
MITK_ERROR << "Cropping working region failed!";
return;
}
}
// initialize region iterator
itk::ImageRegionIterator<TOutputImage> regionIt(m_OutoutImageMaskFineSegmentation, workregion);
for (regionIt.GoToBegin(); !regionIt.IsAtEnd(); ++regionIt)
{
// and set all voxel inside the working region to zero
regionIt.Set(0);
}
}
template <class TInputImage, class TOutputImage>
unsigned int itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::AdjustIteratorMask()
{
typedef itk::ThresholdImageFilter<TOutputImage> ThresholdFilterType;
typedef itk::MinimumMaximumImageFilter<TOutputImage> MaxFilterType;
typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New();
typename MaxFilterType::Pointer maxFilter = MaxFilterType::New();
unsigned int maxValue;
if (!m_DiscardLastPreview)
{
// get the biggest value of the image
maxFilter->SetInput(m_OutoutImageMaskFineSegmentation);
maxFilter->UpdateLargestPossibleRegion();
maxValue = maxFilter->GetMaximum();
}
else
{ // use the last biggest value in the preview. This was set in SetParameterForFineSegmentation(...adjLowerTh...) []
maxValue = m_AdjLowerTh;
}
- // set all values <lower && >upper to zero (thresouldOutside uses < and > NOT <= and >=)
+ // set all values <lower && >upper to zero (thresholdOutside uses < and > NOT <= and >=)
thresholdFilter->SetInput(m_OutoutImageMaskFineSegmentation);
thresholdFilter->SetOutsideValue(0);
thresholdFilter->ThresholdOutside(m_AdjLowerTh, maxValue);
thresholdFilter->UpdateLargestPossibleRegion();
// set all values in between lower and upper (>=lower && <=upper) to the highest value in the image
thresholdFilter->SetInput(thresholdFilter->GetOutput());
thresholdFilter->SetOutsideValue(maxValue);
thresholdFilter->ThresholdOutside(0, m_AdjLowerTh - 1);
thresholdFilter->UpdateLargestPossibleRegion();
m_OutoutImageMaskFineSegmentation = thresholdFilter->GetOutput();
return maxValue;
}
template <class TInputImage, class TOutputImage>
void itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::SetParameterForFineSegmentation(
TOutputImage *iteratorMaskForFineSegmentation,
unsigned int adjLowerTh,
unsigned int adjUpperTh,
itk::Index<3> seedPoint,
bool discardLeafSegmentation)
{
- // just to make sure we´re in the right mode and the mask exsits
+ // just to make sure we´re in the right mode and the mask exists
if (m_FineDetectionMode && iteratorMaskForFineSegmentation)
{
m_OutoutImageMaskFineSegmentation = iteratorMaskForFineSegmentation;
m_AdjLowerTh = adjLowerTh;
m_AdjUpperTh = adjUpperTh; // still needed?
m_SeedPointIndex = seedPoint;
m_DiscardLastPreview = discardLeafSegmentation;
}
else
{
if (!m_FineDetectionMode)
{
MITK_ERROR << "Fine-detection-segmentation mode not set!";
}
else
{
MITK_ERROR << "Iterator-mask-image not set!";
}
}
}
} // end namespace itk
#endif
diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
index f103d54896..852c3ad118 100644
--- a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
+++ b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
@@ -1,536 +1,536 @@
/*============================================================================
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.
============================================================================*/
/*===================================================================
This file is based heavily on a corresponding ITK filter.
===================================================================*/
#ifndef __itkContourExtractor2DImageFilter_txx
#define __itkContourExtractor2DImageFilter_txx
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkContourExtractor2DImageFilter.h"
#include "itkProgressReporter.h"
#include <cmath>
namespace itk
{
// Constructor
template <class TInputImage>
ContourExtractor2DImageFilter<TInputImage>::ContourExtractor2DImageFilter()
{
this->m_ContourValue = NumericTraits<InputRealType>::Zero;
this->m_ReverseContourOrientation = false;
this->m_VertexConnectHighPixels = false;
this->m_UseCustomRegion = false;
this->m_NumberOfContoursCreated = 0;
}
// Destructor
template <class TInputImage>
ContourExtractor2DImageFilter<TInputImage>::~ContourExtractor2DImageFilter()
{
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::GenerateData()
{
// Make sure the structures for containing, looking up, and numbering the
// growing contours are empty and ready.
m_Contours.clear();
m_ContourStarts.clear();
m_ContourEnds.clear();
m_NumberOfContoursCreated = 0;
// Set up an iterator to "march the squares" across the image.
// We associate each 2px-by-2px square with the pixel in the upper left of
// that square. We then iterate across the image, examining these 2x2 squares
// and building the contour. By iterating the upper-left pixel of our
// "current square" across every pixel in the image except those on the
// bottom row and rightmost column, we have visited every valid square in the
// image.
InputRegionType region = this->GetInput()->GetRequestedRegion();
typename InputRegionType::SizeType shrunkSize = region.GetSize();
shrunkSize[0] -= 1;
shrunkSize[1] -= 1;
InputRegionType shrunkRegion(region.GetIndex(), shrunkSize);
// Set up a progress reporter
ProgressReporter progress(this, 0, shrunkRegion.GetNumberOfPixels());
// A 1-pixel radius sets up a neighborhood with the following indices:
// 0 1 2
// 3 4 5
// 6 7 8
// We are interested only in the square of 4,5,7,8 which is the 2x2 square
// with the center pixel at the top-left. So we only activate the
- // coresponding offsets, and only query pixels 4, 5, 7, and 8 with the
+ // corresponding offsets, and only query pixels 4, 5, 7, and 8 with the
// iterator's GetPixel method.
typedef ConstShapedNeighborhoodIterator<InputImageType> SquareIterator;
typename SquareIterator::RadiusType radius = {{1, 1}};
SquareIterator it(radius, this->GetInput(), shrunkRegion);
InputOffsetType none = {{0, 0}};
InputOffsetType right = {{1, 0}};
InputOffsetType down = {{0, 1}};
InputOffsetType diag = {{1, 1}};
it.ActivateOffset(none);
it.ActivateOffset(right);
it.ActivateOffset(down);
it.ActivateOffset(diag);
for (it.GoToBegin(); !it.IsAtEnd(); ++it)
{
// There are sixteen different possible square types, diagramed below.
// A + indicates that the vertex is above the contour value, and a -
// indicates that the vertex is below or equal to the contour value.
// The vertices of each square are here numbered:
// 01
// 23
// and treated as a binary value with the bits in that order. Thus each
// square can be so numbered:
// 0-- 1+- 2-+ 3++ 4-- 5+- 6-+ 7++
// -- -- -- -- +- +- +- +-
//
// 8-- 9+- 10-+ 11++ 12-- 13+- 14-+ 15++
// -+ -+ -+ -+ ++ ++ ++ ++
//
// The position of the line segment that cuts through (or doesn't, in case
// 0 and 15) each square is clear, except in cases 6 and 9. In this case,
// where the segments are placed is determined by
// m_VertexConnectHighPixels. If m_VertexConnectHighPixels is false, then
// lines like are drawn through square 6, and lines like are drawn through
// square 9. Otherwise, the situation is reversed.
// Finally, recall that we draw the lines so that (moving from tail to
// head) the lower-valued pixels are on the left of the line. So, for
// example, case 1 entails a line slanting from the middle of the top of
// the square to the middle of the left side of the square.
// (1) Determine what number square we are currently inspecting. Remember
// that as far as the neighborhood iterator is concerned, our square
// 01 is numbered as 45
// 23 78
InputPixelType v0, v1, v2, v3;
v0 = it.GetPixel(4);
v1 = it.GetPixel(5);
v2 = it.GetPixel(7);
v3 = it.GetPixel(8);
InputIndexType index = it.GetIndex();
unsigned char squareCase = 0;
if (v0 > m_ContourValue)
squareCase += 1;
if (v1 > m_ContourValue)
squareCase += 2;
if (v2 > m_ContourValue)
squareCase += 4;
if (v3 > m_ContourValue)
squareCase += 8;
// Set up macros to find the ContinuousIndex where the contour intersects
// one of the sides of the square. Normally macros should, of course, be
// eschewed, but since this is an inner loop not calling the function four
// times when two would do is probably worth while. Plus, copy-pasting
// these into the switch below is even worse. InterpolateContourPosition
// takes the values at two vertices, the index of the first vertex, and the
// offset between the two vertices.
#define TOP_ this->InterpolateContourPosition(v0, v1, index, right)
#define BOTTOM_ this->InterpolateContourPosition(v2, v3, index + down, right)
#define LEFT_ this->InterpolateContourPosition(v0, v2, index, down)
#define RIGHT_ this->InterpolateContourPosition(v1, v3, index + right, down)
// (2) Add line segments to the growing contours as defined by the cases.
// AddSegment takes a "from" vertex and a "to" vertex, and adds it to the
// a growing contour, creates a new contour, or merges two together.
switch (squareCase)
{
case 0: // no line
break;
case 1: // top to left
this->AddSegment(TOP_, LEFT_);
break;
case 2: // right to top
this->AddSegment(RIGHT_, TOP_);
break;
case 3: // right to left
this->AddSegment(RIGHT_, LEFT_);
break;
case 4: // left to bottom
this->AddSegment(LEFT_, BOTTOM_);
break;
case 5: // top to bottom
this->AddSegment(TOP_, BOTTOM_);
break;
case 6:
if (m_VertexConnectHighPixels)
{
// left to top
this->AddSegment(LEFT_, TOP_);
// right to bottom
this->AddSegment(RIGHT_, BOTTOM_);
}
else
{
// right to top
this->AddSegment(RIGHT_, TOP_);
// left to bottom
this->AddSegment(LEFT_, BOTTOM_);
}
break;
case 7: // right to bottom
this->AddSegment(RIGHT_, BOTTOM_);
break;
case 8: // bottom to right
this->AddSegment(BOTTOM_, RIGHT_);
break;
case 9:
if (m_VertexConnectHighPixels)
{
// top to right
this->AddSegment(TOP_, RIGHT_);
// bottom to left
this->AddSegment(BOTTOM_, LEFT_);
}
else
{
// top to left
this->AddSegment(TOP_, LEFT_);
// bottom to right
this->AddSegment(BOTTOM_, RIGHT_);
}
break;
case 10: // bottom to top
this->AddSegment(BOTTOM_, TOP_);
break;
case 11: // bottom to left
this->AddSegment(BOTTOM_, LEFT_);
break;
case 12: // left to right
this->AddSegment(LEFT_, RIGHT_);
break;
case 13: // top to right
this->AddSegment(TOP_, RIGHT_);
break;
case 14: // left to top
this->AddSegment(LEFT_, TOP_);
break;
case 15: // no line
break;
} // switch squareCase
progress.CompletedPixel();
} // iteration
// Now create the outputs paths from the deques we've been using.
this->FillOutputs();
m_Contours.clear();
m_ContourStarts.clear();
m_ContourEnds.clear();
m_NumberOfContoursCreated = 0;
}
template <class TInputImage>
inline typename ContourExtractor2DImageFilter<TInputImage>::VertexType
ContourExtractor2DImageFilter<TInputImage>::InterpolateContourPosition(InputPixelType fromValue,
InputPixelType toValue,
InputIndexType fromIndex,
InputOffsetType toOffset)
{
VertexType output;
// Now calculate the fraction of the way from 'from' to 'to' that the contour
// crosses. Interpolate linearly: y = v0 + (v1 - v0) * x, and solve for the
// x that gives y = m_ContourValue: x = (m_ContourValue - v0) / (v1 - v0).
// This assumes that v0 and v1 are separated by exactly ONE unit. So the to
// Offset. value must have exactly one component 1 and the other component 0.
// Also this assumes that fromValue and toValue are different. Otherwise we
// can't interpolate anything!
itkAssertOrThrowMacro((fromValue != toValue), "source and destination are the same");
itkAssertOrThrowMacro(((toOffset[0] == 0 && toOffset[1] == 1) || (toOffset[0] == 1 && toOffset[1] == 0)),
"toOffset has unexpected values");
double x =
(m_ContourValue - static_cast<InputRealType>(fromValue)) / (toValue - static_cast<InputRealType>(fromValue));
output[0] = fromIndex[0] + x * toOffset[0];
output[1] = fromIndex[1] + x * toOffset[1];
return output;
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::AddSegment(VertexType from, VertexType to)
{
if (from == to)
{
// Arc is degenerate: ignore, and the from/two point will be connected
// later by other squares. Degeneracy happens when (and only when) a square
// has exactly one vertex that is the contour value, and the rest are above
// that value.
return;
}
// Try to find an existing contour that starts where the new segment ends.
VertexMapIterator newTail = m_ContourStarts.find(to);
// Try to find an existing contour that ends where the new segment starts.
VertexMapIterator newHead = m_ContourEnds.find(from);
if (newTail != m_ContourStarts.end() && newHead != m_ContourEnds.end())
{
// We need to connect these two contours. The act of connecting them will
// add the needed arc.
auto tail = newTail->second;
itkAssertOrThrowMacro((tail->front() == to), "End doesn't match Beginning");
auto head = newHead->second;
itkAssertOrThrowMacro((head->back() == from), "Beginning doesn't match End");
if (head == tail)
{
// We've closed a contour. Add the end point, and remove from the maps
head->push_back(to);
m_ContourStarts.erase(newTail);
// erase the front of tail. Because head and tail are the same contour,
// don't worry about erasing the front of head!
m_ContourEnds.erase(newHead); // erase the end of head/tail.
}
else
{
// We have found two distinct contours that need to be joined. Careful
// here: we want to keep the first segment in the list when merging so
// that contours are always returned in top-to-bottom, right-to-left
// order (with regard to the image pixel found to be inside the contour).
if (tail->m_ContourNumber > head->m_ContourNumber)
{
// if tail was created later than head...
// Copy tail to the end of head and remove
// tail from everything.
head->insert(head->end(), tail->begin(), tail->end());
// Now remove 'tail' from the list and the maps because it has been
// subsumed.
m_ContourStarts.erase(newTail);
int erased = m_ContourEnds.erase(tail->back());
// There should be exactly one entry in the hash for that endpoint
if (erased != 1)
{
itkWarningMacro(<< "There should be exactly one entry in the hash for that endpoint, but there are "
<< erased);
}
m_Contours.erase(tail); // remove from the master list
// Now remove the old end of 'head' from the ends map and add
// the new end.
m_ContourEnds.erase(newHead);
m_ContourEnds.insert(VertexContourRefPair(head->back(), head));
}
else
{
// Copy head to the beginning of tail and remove
// head from everything.
tail->insert(tail->begin(), head->begin(), head->end());
// Now remove 'head' from the list and the maps because
// it has been subsumed.
m_ContourEnds.erase(newHead);
int erased = m_ContourStarts.erase(head->front());
if (erased != 1)
{
itkWarningMacro(<< "There should be exactly one entry in the hash for that endpoint, but there are "
<< erased);
}
m_Contours.erase(head); // remove from the master list
// Now remove the old start of 'tail' from the starts map and
// add the new start.
m_ContourStarts.erase(newTail);
m_ContourStarts.insert(VertexContourRefPair(tail->front(), tail));
}
}
}
else if (newTail == m_ContourStarts.end() && newHead == m_ContourEnds.end())
{
// No contours found: add a new one.
// Make it on the heap. It will be copied into m_Contours.
ContourType contour;
// Add the endpoints
contour.push_front(from);
contour.push_back(to);
contour.m_ContourNumber = m_NumberOfContoursCreated++;
// Add the contour to the end of the list and get a reference to it.
m_Contours.push_back(contour);
// recall that end() is an iterator to one past the back!
auto newContour = --m_Contours.end();
// add the endpoints and an iterator pointing to the contour
// in the list to the maps.
m_ContourStarts.insert(VertexContourRefPair(from, newContour));
m_ContourEnds.insert(VertexContourRefPair(to, newContour));
}
else if (newTail != m_ContourStarts.end() && newHead == m_ContourEnds.end())
{
// Found a single contour to which the new arc should be prepended.
auto tail = newTail->second;
itkAssertOrThrowMacro((tail->front() == to), "End doesn't match Beginning");
tail->push_front(from);
// erase the old start of this contour
m_ContourStarts.erase(newTail);
// Now add the new start of this contour.
m_ContourStarts.insert(VertexContourRefPair(from, tail));
}
else if (newTail == m_ContourStarts.end() && newHead != m_ContourEnds.end())
{
// Found a single contour to which the new arc should be appended.
auto head = newHead->second;
itkAssertOrThrowMacro((head->back() == from), "Beginning doesn't match End");
head->push_back(to);
// erase the old end of this contour
m_ContourEnds.erase(newHead);
// Now add the new start of this contour.
m_ContourEnds.insert(VertexContourRefPair(to, head));
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::FillOutputs()
{
this->SetNumberOfIndexedOutputs(m_Contours.size());
int i = 0;
for (auto it = m_Contours.begin(); it != m_Contours.end(); it++, i++)
{
OutputPathPointer output = this->GetOutput(i);
if (output.IsNull())
{
// Static cast is OK because we know PathSource will make its templated
// class type
output = static_cast<OutputPathType *>(this->MakeOutput(i).GetPointer());
this->SetNthOutput(i, output.GetPointer());
}
typename VertexListType::Pointer path = const_cast<VertexListType *>(output->GetVertexList());
path->Initialize();
path->reserve(it->size()); // use std::vector version of 'reserve()'
// instead of VectorContainer::Reserve() to work around
// the fact that the latter is essentially std::vector::resize(),
// which is not what we want.
// Now put all the points from the contour deque into the path and
// mark output as modified
typedef typename ContourType::const_iterator ConstIteratorType;
if (m_ReverseContourOrientation)
{
ConstIteratorType itC = (*it).end();
do
{
itC--;
path->push_back(*itC);
} while (itC != (*it).begin());
}
else
{
ConstIteratorType itC = (*it).begin();
while (itC != (*it).end())
{
path->push_back(*itC);
itC++;
}
}
output->Modified();
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::SetRequestedRegion(const InputRegionType region)
{
itkDebugMacro("setting RequestedRegion to " << region);
m_UseCustomRegion = true;
if (this->m_RequestedRegion != region)
{
this->m_RequestedRegion = region;
this->Modified();
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::ClearRequestedRegion()
{
itkDebugMacro("Clearing RequestedRegion.");
if (this->m_UseCustomRegion == true)
{
this->m_UseCustomRegion = false;
this->Modified();
}
}
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::GenerateInputRequestedRegion()
{
InputImageType *input = const_cast<InputImageType *>(this->GetInput());
if (!input)
return;
if (m_UseCustomRegion)
{
InputRegionType requestedRegion = m_RequestedRegion;
if (requestedRegion.Crop(input->GetLargestPossibleRegion()))
{
input->SetRequestedRegion(requestedRegion);
return;
}
else
{
// Couldn't crop the region (requested region is outside the largest
// possible region). Throw an exception.
// store what we tried to request (prior to trying to crop)
input->SetRequestedRegion(requestedRegion);
// build an exception
InvalidRequestedRegionError e(__FILE__, __LINE__);
e.SetLocation(ITK_LOCATION);
e.SetDescription("Requested region is outside the largest possible region.");
e.SetDataObject(input);
throw e;
}
}
else
{
input->SetRequestedRegion(input->GetLargestPossibleRegion());
}
}
/**
* Standard "PrintSelf" method
*/
template <class TInputImage>
void ContourExtractor2DImageFilter<TInputImage>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "ReverseContourOrientation: " << m_ReverseContourOrientation << std::endl;
os << indent << "VertexConnectHighPixels: " << m_VertexConnectHighPixels << std::endl;
os << indent << "UseCustomRegion: " << m_UseCustomRegion << std::endl;
os << indent << "NumericTraits: " << m_UseCustomRegion << std::endl;
os << indent << "NumberOfContoursCreated: " << m_NumberOfContoursCreated << std::endl;
if (m_UseCustomRegion)
{
os << indent << "Custom region: " << m_RequestedRegion << std::endl;
}
typedef typename NumericTraits<InputRealType>::PrintType InputRealPrintType;
os << indent << "Contour value: " << static_cast<InputRealPrintType>(m_ContourValue) << std::endl;
}
} // end namespace itk
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h b/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h
index a19f46b578..4257bc9f32 100644
--- a/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h
+++ b/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h
@@ -1,100 +1,100 @@
/*============================================================================
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 mitkContourModelSetToImageFilter_h
#define mitkContourModelSetToImageFilter_h
#include <MitkSegmentationExports.h>
#include <mitkImageSource.h>
namespace mitk
{
class ContourModelSet;
/**
* @brief Fills a given mitk::ContourModelSet into a given mitk::Image
* @ingroup Process
*/
class MITKSEGMENTATION_EXPORT ContourModelSetToImageFilter : public ImageSource
{
public:
mitkClassMacro(ContourModelSetToImageFilter, ImageSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
virtual void SetMakeOutputBinary(bool makeOutputBinary);
virtual void SetMakeOutputLabelPixelType(bool makeOutputLabelPixelType);
itkSetMacro(PaintingPixelValue, int);
itkSetMacro(TimeStep, unsigned int);
itkGetMacro(MakeOutputBinary, bool);
itkGetMacro(MakeOutputLabelPixelType, bool);
itkGetMacro(PaintingPixelValue, int);
itkBooleanMacro(MakeOutputBinary);
itkBooleanMacro(MakeOutputLabelPixelType);
/**
* Allocates a new output object and returns it. Currently the
* index idx is not evaluated.
* @param idx the index of the output for which an object should be created
* @returns the new object
*/
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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
const mitk::ContourModelSet *GetInput(void);
using itk::ProcessObject::SetInput;
virtual void SetInput(const mitk::ContourModelSet *input);
/**
* @brief Set the image which will be used to initialize the output of this filter.
* @param refImage the image used to initialize the output image
*/
void SetImage(const mitk::Image *refImage);
const mitk::Image *GetImage(void);
protected:
ContourModelSetToImageFilter();
~ContourModelSetToImageFilter() override;
/**
* @brief Initializes the volume of the output image with zeros
*/
void InitializeOutputEmpty();
bool m_MakeOutputBinary;
bool m_MakeOutputLabelPixelType;
int m_PaintingPixelValue;
unsigned int m_TimeStep;
const mitk::Image *m_ReferenceImage;
};
}
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
index 0aec6159c9..3d0ba2fd76 100644
--- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
+++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
@@ -1,484 +1,484 @@
/*============================================================================
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 "mitkCorrectorAlgorithm.h"
#include "mitkContourUtils.h"
#include "mitkITKImageImport.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageDataItem.h"
#include <mitkContourModelUtils.h>
#include "itkCastImageFilter.h"
#include "itkImageDuplicator.h"
#include "itkImageRegionIterator.h"
mitk::CorrectorAlgorithm::CorrectorAlgorithm() : ImageToImageFilter(), m_FillColor(1), m_EraseColor(0)
{
}
mitk::CorrectorAlgorithm::~CorrectorAlgorithm()
{
}
template <typename TPixel, unsigned int VDimensions>
void ConvertBackToCorrectPixelType(
itk::Image<TPixel, VDimensions> *,
mitk::Image::Pointer target,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer segmentationPixelTypeImage)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2> InputImageType;
typedef itk::Image<TPixel, 2> OutputImageType;
typedef itk::CastImageFilter<InputImageType, OutputImageType> CastImageFilterType;
typename CastImageFilterType::Pointer castImageFilter = CastImageFilterType::New();
castImageFilter->SetInput(segmentationPixelTypeImage);
castImageFilter->Update();
typename OutputImageType::Pointer tempItkImage = castImageFilter->GetOutput();
tempItkImage->DisconnectPipeline();
mitk::CastToMitkImage(tempItkImage, target);
}
void mitk::CorrectorAlgorithm::GenerateData()
{
Image::Pointer inputImage = ImageToImageFilter::GetInput(0);
if (inputImage.IsNull() || inputImage->GetDimension() != 2)
{
itkExceptionMacro("CorrectorAlgorithm needs a 2D image as input.");
}
if (m_Contour.IsNull())
{
itkExceptionMacro("CorrectorAlgorithm needs a Contour object as input.");
}
// copy the input (since m_WorkingImage will be changed later)
m_WorkingImage = inputImage;
TimeGeometry::Pointer originalGeometry = nullptr;
if (inputImage->GetTimeGeometry())
{
originalGeometry = inputImage->GetTimeGeometry()->Clone();
m_WorkingImage->SetTimeGeometry(originalGeometry);
}
else
{
itkExceptionMacro("Original image does not have a 'Time sliced geometry'! Cannot copy.");
}
Image::Pointer temporarySlice;
- // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relys on that data type)
+ // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relies on that data type)
{
itk::Image<DefaultSegmentationDataType, 2>::Pointer correctPixelTypeImage;
CastToItkImage(m_WorkingImage, correctPixelTypeImage);
assert(correctPixelTypeImage.IsNotNull());
// possible bug in CastToItkImage ?
// direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in
// mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479:
// virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0
// && aSpacing[2]>0' failed
// solution here: we overwrite it with an unity matrix
itk::Image<DefaultSegmentationDataType, 2>::DirectionType imageDirection;
imageDirection.SetIdentity();
// correctPixelTypeImage->SetDirection(imageDirection);
temporarySlice = this->GetOutput();
// temporarySlice = ImportItkImage( correctPixelTypeImage );
// m_FillColor = 1;
m_EraseColor = 0;
ImprovedHeimannCorrectionAlgorithm(correctPixelTypeImage);
// this is suboptimal, needs to be kept synchronous to DefaultSegmentationDataType
if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT)
{ // the cast at the beginning did not copy the data
CastToMitkImage(correctPixelTypeImage, temporarySlice);
}
else
{ // it did copy the data and cast the pixel type
AccessByItk_n(m_WorkingImage, ConvertBackToCorrectPixelType, (temporarySlice, correctPixelTypeImage));
}
}
temporarySlice->SetTimeGeometry(originalGeometry);
}
template <typename ScalarType>
itk::Index<2> mitk::CorrectorAlgorithm::ensureIndexInImage(ScalarType i0, ScalarType i1)
{
itk::Index<2> toReturn;
itk::Size<5> size = m_WorkingImage->GetLargestPossibleRegion().GetSize();
toReturn[0] = std::min((ScalarType)(size[0] - 1), std::max((ScalarType)0.0, i0));
toReturn[1] = std::min((ScalarType)(size[1] - 1), std::max((ScalarType)0.0, i1));
return toReturn;
}
bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm(
itk::Image<DefaultSegmentationDataType, 2>::Pointer pic)
{
/*!
Some documentation (not by the original author)
TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line.
There should be different results, depending on the line's properties:
1. Without any prior segmentation, the start point and the end point of the drawn line will be
connected to a contour and the area enclosed by the contour will be marked as segmentation.
2. When the whole line is inside a segmentation, start and end point will be connected to
a contour and the area of this contour will be subtracted from the segmentation.
3. When the line starts inside a segmentation and ends outside with only a single
transition from segmentation to no-segmentation, nothing will happen.
4. When there are multiple transitions between inside-segmentation and
outside-segmentation, the line will be divided in so called segments. Each segment is
either fully inside or fully outside a segmentation. When it is inside a segmentation, its
enclosed area will be subtracted from the segmentation. When the segment is outside a
segmentation, its enclosed area it will be added to the segmentation.
The algorithm is described in full length in Tobias Heimann's diploma thesis
(MBI Technical Report 145, p. 37 - 40).
*/
ContourModel::Pointer projectedContour =
mitk::ContourModelUtils::ProjectContourTo2DSlice(m_WorkingImage, m_Contour);
if (projectedContour.IsNull() || projectedContour->GetNumberOfVertices() < 2)
return false;
// Read the first point of the contour
auto contourIter = projectedContour->Begin();
if (contourIter == projectedContour->End())
return false;
itk::Index<2> previousIndex;
previousIndex = ensureIndexInImage((*contourIter)->Coordinates[0], (*contourIter)->Coordinates[1]);
++contourIter;
int currentColor = (pic->GetPixel(previousIndex) == m_FillColor);
TSegData currentSegment;
bool firstSegment = true;
auto contourEnd = projectedContour->End();
for (; contourIter != contourEnd; ++contourIter)
{
// Get current point
itk::Index<2> currentIndex;
currentIndex = ensureIndexInImage((*contourIter)->Coordinates[0] + 0.5, (*contourIter)->Coordinates[1] + 0.5);
// Calculate length and slope
double slopeX = currentIndex[0] - previousIndex[0];
double slopeY = currentIndex[1] - previousIndex[1];
double length = std::sqrt(slopeX * slopeX + slopeY * slopeY);
double deltaX = slopeX / length;
double deltaY = slopeY / length;
for (double i = 0; i <= length && length > 0; i += 1)
{
itk::Index<2> temporaryIndex;
temporaryIndex = ensureIndexInImage(previousIndex[0] + deltaX * i, previousIndex[1] + deltaY * i);
if (!pic->GetLargestPossibleRegion().IsInside(temporaryIndex))
continue;
if ((pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor)
{
currentSegment.points.push_back(temporaryIndex);
if (!firstSegment)
{
ModifySegment(currentSegment, pic);
}
else
{
firstSegment = false;
}
currentSegment = TSegData();
currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor);
}
currentSegment.points.push_back(temporaryIndex);
}
previousIndex = currentIndex;
}
return true;
}
void mitk::CorrectorAlgorithm::ColorSegment(
const mitk::CorrectorAlgorithm::TSegData &segment,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor);
int color = 0;
if (colorMode)
color = m_EraseColor;
else
color = m_FillColor;
std::vector<itk::Index<2>>::const_iterator indexIterator;
std::vector<itk::Index<2>>::const_iterator indexEnd;
indexIterator = segment.points.begin();
indexEnd = segment.points.end();
for (; indexIterator != indexEnd; ++indexIterator)
{
pic->SetPixel(*indexIterator, color);
}
}
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer mitk::CorrectorAlgorithm::CloneImage(
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2> ItkImageType;
typedef itk::ImageDuplicator<ItkImageType> DuplicatorType;
DuplicatorType::Pointer duplicator = DuplicatorType::New();
duplicator->SetInputImage(pic);
duplicator->Update();
return duplicator->GetOutput();
}
itk::Index<2> mitk::CorrectorAlgorithm::GetFirstPoint(
const mitk::CorrectorAlgorithm::TSegData &segment,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor);
std::vector<itk::Index<2>>::const_iterator indexIterator;
std::vector<itk::Index<2>>::const_iterator indexEnd;
indexIterator = segment.points.begin();
indexEnd = segment.points.end();
itk::Index<2> index;
for (; indexIterator != indexEnd; ++indexIterator)
{
for (int xOffset = -1; xOffset < 2; ++xOffset)
{
for (int yOffset = -1; yOffset < 2; ++yOffset)
{
index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset);
if ((pic->GetPixel(index) == m_FillColor) != colorMode)
{
return index;
}
}
}
}
mitkThrow() << "No Starting point is found next to the curve.";
}
std::vector<itk::Index<2>> mitk::CorrectorAlgorithm::FindSeedPoints(
const mitk::CorrectorAlgorithm::TSegData &segment,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer ItkImagePointerType;
std::vector<itk::Index<2>> seedPoints;
try
{
itk::Index<2> firstPoint = GetFirstPoint(segment, pic);
seedPoints.push_back(firstPoint);
}
catch (const mitk::Exception&)
{
return seedPoints;
}
if (segment.points.size() < 4)
return seedPoints;
std::vector<itk::Index<2>>::const_iterator indexIterator;
std::vector<itk::Index<2>>::const_iterator indexEnd;
indexIterator = segment.points.begin();
indexEnd = segment.points.end();
ItkImagePointerType listOfPoints = CloneImage(pic);
listOfPoints->FillBuffer(0);
listOfPoints->SetPixel(seedPoints[0], 1);
for (; indexIterator != indexEnd; ++indexIterator)
{
listOfPoints->SetPixel(*indexIterator, 2);
}
indexIterator = segment.points.begin();
indexIterator++;
indexIterator++;
indexEnd--;
indexEnd--;
for (; indexIterator != indexEnd; ++indexIterator)
{
bool pointFound = true;
while (pointFound)
{
pointFound = false;
itk::Index<2> index;
itk::Index<2> index2;
for (int xOffset = -1; xOffset < 2; ++xOffset)
{
for (int yOffset = -1; yOffset < 2; ++yOffset)
{
index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset);
index2 = index;
if (listOfPoints->GetPixel(index2) > 0)
continue;
index[0] = index[0] - 1;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
index[0] = index[0] + 2;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
index[0] = index[0] - 1;
index[1] = index[1] - 1;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
index[1] = index[1] + 2;
index = ensureIndexInImage(index[0], index[1]);
if (listOfPoints->GetPixel(index) == 1)
{
pointFound = true;
seedPoints.push_back(index2);
listOfPoints->SetPixel(index2, 1);
continue;
}
}
}
}
}
return seedPoints;
}
int mitk::CorrectorAlgorithm::FillRegion(
const std::vector<itk::Index<2>> &seedPoints,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer pic)
{
int numberOfPixel = 0;
int mode = (pic->GetPixel(seedPoints[0]) == m_FillColor);
int drawColor = m_FillColor;
if (mode)
{
drawColor = m_EraseColor;
}
std::vector<itk::Index<2>> workPoints;
workPoints = seedPoints;
// workPoints.push_back(seedPoints[0]);
while (workPoints.size() > 0)
{
itk::Index<2> currentIndex = workPoints.back();
workPoints.pop_back();
if ((pic->GetPixel(currentIndex) == m_FillColor) == mode)
++numberOfPixel;
pic->SetPixel(currentIndex, drawColor);
currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1]);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
currentIndex = ensureIndexInImage(currentIndex[0] + 2, currentIndex[1]);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1] - 1);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
currentIndex = ensureIndexInImage(currentIndex[0], currentIndex[1] + 2);
if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode)
workPoints.push_back(currentIndex);
}
return numberOfPixel;
}
void mitk::CorrectorAlgorithm::OverwriteImage(
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer source,
itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2>::Pointer target)
{
typedef itk::Image<mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2> ItkImageType;
typedef itk::ImageRegionIterator<ItkImageType> ImageIteratorType;
ImageIteratorType sourceIter(source, source->GetLargestPossibleRegion());
ImageIteratorType targetIter(target, target->GetLargestPossibleRegion());
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
}
bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment,
itk::Image<DefaultSegmentationDataType, 2>::Pointer pic)
{
typedef itk::Image<DefaultSegmentationDataType, 2>::Pointer ItkImagePointerType;
ItkImagePointerType firstSideImage = CloneImage(pic);
ColorSegment(segment, firstSideImage);
ItkImagePointerType secondSideImage = CloneImage(firstSideImage);
std::vector<itk::Index<2>> seedPoints = FindSeedPoints(segment, firstSideImage);
if (seedPoints.size() < 1)
return false;
int firstSidePixel = FillRegion(seedPoints, firstSideImage);
std::vector<itk::Index<2>> secondSeedPoints = FindSeedPoints(segment, firstSideImage);
if (secondSeedPoints.size() < 1)
return false;
int secondSidePixel = FillRegion(secondSeedPoints, secondSideImage);
if (firstSidePixel < secondSidePixel)
{
OverwriteImage(firstSideImage, pic);
}
else
{
OverwriteImage(secondSideImage, pic);
}
return true;
}
diff --git a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h
index 5790ebb16b..e4bbc93c5c 100644
--- a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h
+++ b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h
@@ -1,158 +1,158 @@
/*============================================================================
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 mitkImageLiveWireContourModelFilter_h
#define mitkImageLiveWireContourModelFilter_h
#include "mitkCommon.h"
#include "mitkContourModel.h"
#include "mitkContourModelSource.h"
#include <MitkSegmentationExports.h>
#include <mitkImage.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <itkShortestPathCostFunctionLiveWire.h>
#include <itkShortestPathImageFilter.h>
namespace mitk
{
/**
\brief Calculates a LiveWire contour between two points in an image.
- For defining costs between two pixels specific features are extraced from the image and tranformed into a single cost
+ For defining costs between two pixels specific features are extracted from the image and transformed into a single cost
value.
\sa ShortestPathCostFunctionLiveWire
- The filter is able to create dynamic cost tranfer map and thus use on the fly training.
+ The filter is able to create dynamic cost transfer map and thus use on the fly training.
\note On the fly training will only be used for next update.
The computation uses the last calculated segment to map cost according to features in the area of the segment.
Caution: time support currently not available. Filter will always work on the first
timestep in its current implementation.
\ingroup ContourModelFilters
\ingroup Process
*/
class MITKSEGMENTATION_EXPORT ImageLiveWireContourModelFilter : public ContourModelSource
{
public:
mitkClassMacro(ImageLiveWireContourModelFilter, ContourModelSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef ContourModel OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef mitk::Image InputType;
typedef itk::Image<float, 2> InternalImageType;
typedef itk::ShortestPathImageFilter<InternalImageType, InternalImageType> ShortestPathImageFilterType;
typedef itk::ShortestPathCostFunctionLiveWire<InternalImageType> CostFunctionType;
typedef std::vector<itk::Index<2>> ShortestPathType;
/** \brief start point in world coordinates*/
itkSetMacro(StartPoint, mitk::Point3D);
itkGetMacro(StartPoint, mitk::Point3D);
/** \brief end point in woorld coordinates*/
itkSetMacro(EndPoint, mitk::Point3D);
itkGetMacro(EndPoint, mitk::Point3D);
- /** \brief Create dynamic cost tranfer map - use on the fly training.
+ /** \brief Create dynamic cost transfer map - use on the fly training.
\note On the fly training will be used for next update only.
The computation uses the last calculated segment to map cost according to features in the area of the segment.
*/
itkSetMacro(UseDynamicCostMap, bool);
itkGetMacro(UseDynamicCostMap, bool);
/** \brief Clear all repulsive points used in the cost function
*/
void ClearRepulsivePoints();
/** \brief Set a vector with repulsive points to use in the cost function
*/
void SetRepulsivePoints(const ShortestPathType &points);
/** \brief Add a single repulsive point to the cost function
*/
void AddRepulsivePoint(const itk::Index<2> &idx);
/** \brief Remove a single repulsive point from the cost function
*/
void RemoveRepulsivePoint(const itk::Index<2> &idx);
virtual void SetInput(const InputType *input);
using Superclass::SetInput;
virtual void SetInput(unsigned int idx, const InputType *input);
const InputType *GetInput(void);
const InputType *GetInput(unsigned int idx);
virtual OutputType *GetOutput();
virtual void DumpMaskImage();
- /** \brief Create dynamic cost tranfer map - on the fly training*/
+ /** \brief Create dynamic cost transfer map - on the fly training*/
bool CreateDynamicCostMap(mitk::ContourModel *path = nullptr);
void SetUseCostFunction(bool doUseCostFunction) { m_ShortestPathFilter->SetUseCostFunction(doUseCostFunction); };
protected:
ImageLiveWireContourModelFilter();
~ImageLiveWireContourModelFilter() override;
void GenerateOutputInformation() override{};
void GenerateData() override;
void UpdateLiveWire();
/** \brief start point in worldcoordinates*/
mitk::Point3D m_StartPoint;
/** \brief end point in woorldcoordinates*/
mitk::Point3D m_EndPoint;
/** \brief Start point in index*/
mitk::Point3D m_StartPointInIndex;
/** \brief End point in index*/
mitk::Point3D m_EndPointInIndex;
/** \brief The cost function to compute costs between two pixels*/
CostFunctionType::Pointer m_CostFunction;
/** \brief Shortest path filter according to cost function m_CostFunction*/
ShortestPathImageFilterType::Pointer m_ShortestPathFilter;
- /** \brief Flag to use a dynmic cost map or not*/
+ /** \brief Flag to use a dynamic cost map or not*/
bool m_UseDynamicCostMap;
unsigned int m_TimeStep;
template <typename TPixel, unsigned int VImageDimension>
void ItkPreProcessImage(const itk::Image<TPixel, VImageDimension> *inputImage);
template <typename TPixel, unsigned int VImageDimension>
void CreateDynamicCostMapByITK(const itk::Image<TPixel, VImageDimension> *inputImage,
mitk::ContourModel *path = nullptr);
InternalImageType::Pointer m_InternalImage;
};
}
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h
index 39b3972a4b..acfdba0b55 100644
--- a/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h
+++ b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h
@@ -1,164 +1,164 @@
/*============================================================================
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 mitkManualSegmentationToSurfaceFilter_h
#define mitkManualSegmentationToSurfaceFilter_h
#include <MitkSegmentationExports.h>
#include <mitkImageToSurfaceFilter.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkImageMedian3D.h>
#include <vtkImageResample.h>
#include <vtkImageThreshold.h>
namespace mitk
{
/**
* @brief Supplies a 3D surface from pre-processed segmentation.
*
* The resulting surface depends on a filter pipeline based on vtkMedian (1) and a Gaussian filter with
* vtkImageGaussianSmooth (2).
* All voxel can be changed to an isotropic representation of the
- * image (ATTANTION: the number of voxels in the will change). The
+ * image (ATTENTION: the number of voxels in the image will change). The
* resulting isotropic image has 1mm isotropic voxel by default. But
* can be varied freely.
*
* @ingroup ImageFilters
* @ingroup Process
*/
class MITKSEGMENTATION_EXPORT ManualSegmentationToSurfaceFilter : public ImageToSurfaceFilter
{
public:
mitkClassMacro(ManualSegmentationToSurfaceFilter, ImageToSurfaceFilter);
typedef double vtkDouble;
/**
* Will pre-process a segmentation voxelwise. The segmentation can use
* a hole fill relating a median filter and smooth by a Gaussian
* filter.
* The image can be interpolated to an isotropic image.
* By default every filter is disabled.
* This method calls CreateSurface from mitkImageToSurfaceFilter and
* does not need a manual call since we use Update().
*/
void GenerateData() override;
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* Supplies a method for setting median filter by a bool value.
*/
itkSetMacro(MedianFilter3D, bool);
/**
* Return state if median filter is enabled.
*/
itkGetConstMacro(MedianFilter3D, bool);
/**
* Enable the median filter (first filter in pipeline).
*/
itkBooleanMacro(MedianFilter3D);
/**
* Supplies a method to enable Interpolation.
*/
itkSetMacro(Interpolation, bool);
/**
* Returns activation state of interpolation filter.
*/
itkGetConstMacro(Interpolation, bool);
/**
* Enable the interpolation filter (second filter in pipeline) for
* isotropic voxel.
*/
itkBooleanMacro(Interpolation);
/**
* Supplies a method for Gaussian filter (third filter in pipeline).
*/
itkSetMacro(UseGaussianImageSmooth, bool);
/**
* Returns activation state of standard deviation filter.
*/
itkGetConstMacro(UseGaussianImageSmooth, bool);
/**
* Enables Gaussian image smooth. As well the threshold for the
* CreateSurface() method will raise the threshold to 49 and changes
* the image range set from 0 to 100. It is made for reasons in
* binary images to prevent conflicts with the used filter. There are
* better results for dividing fore- and background.
*/
itkBooleanMacro(UseGaussianImageSmooth);
/**
* Set standard deviation for Gaussian Filter.
* @param _arg by default 1.5
*/
itkSetMacro(GaussianStandardDeviation, double);
/**
* Returns the standard deviation of the Gaussian filter which will be
* used when filter is enabled.
*/
itkGetConstMacro(GaussianStandardDeviation, double);
/**
* Set the Kernel for Median3DFilter. By default kernel is set to 3x3x3.
* If you choose '1' nothing will be processed in this direction.
*/
void SetMedianKernelSize(int x, int y, int z);
/**
* Returns the kernel size in the first direction.
*/
itkGetConstMacro(MedianKernelSizeX, int);
/**
* Returns the kernel size in the second direction.
*/
itkGetConstMacro(MedianKernelSizeY, int);
/**
* Returns the kernel size in the third direction.
*/
itkGetConstMacro(MedianKernelSizeZ, int);
/**
* Set the values for Spacing in X, Y and Z-Dimension
*/
void SetInterpolation(vtkDouble x, vtkDouble y, vtkDouble z);
protected:
ManualSegmentationToSurfaceFilter();
~ManualSegmentationToSurfaceFilter() override;
bool m_MedianFilter3D;
int m_MedianKernelSizeX, m_MedianKernelSizeY, m_MedianKernelSizeZ;
bool m_UseGaussianImageSmooth; // Gaussian Filter
double m_GaussianStandardDeviation;
bool m_Interpolation;
vtkDouble m_InterpolationX;
vtkDouble m_InterpolationY;
vtkDouble m_InterpolationZ;
}; // namespace
}
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h
index 56f821ad56..db4c40ed51 100644
--- a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h
+++ b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h
@@ -1,84 +1,84 @@
/*============================================================================
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 mitkVtkImageOverwrite_h
#define mitkVtkImageOverwrite_h
#include <MitkSegmentationExports.h>
#include <vtkImageReslice.h>
-/** \brief A vtk Filter based on vtkImageReslice with the aditional feature to write a slice into the given input
+/** \brief A vtk Filter based on vtkImageReslice with the additional feature to write a slice into the given input
volume.
All optimizations for e.g. the plane directions or interpolation are stripped away, the algorithm only interpolates
nearest
neighbor and uses the non optimized execute function of vtkImageReslice. Note that any interpolation doesn't make
sense
for round trip use extract->edit->overwrite, because it is nearly impossible to invert the interpolation.
There are two use cases for the Filter which are specified by the overwritemode property:
1)Extract slices from a 3D volume.
Overwritemode = false
In this mode the class can be used like vtkImageReslice. The usual way to do this is:
- Set an image volume as input
- Set the ResliceAxes via SetResliceAxesDirectionCosines and SetResliceAxesOrigin
- Set the the OutputSpacing, OutputOrigin and OutputExtent
- Call Update
2)Overwrite a 3D volume at a given slice.
Overwritemode = true
The handling in this mode is quite similar to the description above with the addition that the
InputSlice needs to be specified via SetInputSlice(vtkImageData*).
- Set the properties mentioned above (Note that SetInput specifies the volume to write to)
- Set the slice to that has to be overwritten in the volume ( SetInputSlice(vtkImageData*)
After calling Update() there is no need to retrieve the output as the input volume is modified.
\sa vtkImageReslice
- (Note that the execute and interpolation functions are no members and thus can not be overriden)
+ (Note that the execute and interpolation functions are no members and thus can not be overridden)
*/
class MITKSEGMENTATION_EXPORT mitkVtkImageOverwrite : public vtkImageReslice
{
public:
static mitkVtkImageOverwrite *New();
vtkTypeMacro(mitkVtkImageOverwrite, vtkImageReslice);
/** \brief Set the mode either to reslice (false) or to overwrite (true).
Default: false
*/
void SetOverwriteMode(bool b);
bool IsOverwriteMode() { return m_Overwrite_Mode; }
/** \brief Set the slice for overwrite mode.
Note:
It is recommend not to use this in reslice mode because otherwise the slice will be modified!
*/
void SetInputSlice(vtkImageData *slice);
protected:
mitkVtkImageOverwrite();
~mitkVtkImageOverwrite() override;
bool m_Overwrite_Mode;
/** Overridden from vtkImageReslice. \sa vtkImageReslice::ThreadedRequestData */
void ThreadedRequestData(vtkInformation *vtkNotUsed(request),
vtkInformationVector **vtkNotUsed(inputVector),
vtkInformationVector *vtkNotUsed(outputVector),
vtkImageData ***inData,
vtkImageData **outData,
int outExt[6],
int id) override;
};
#endif
diff --git a/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp
index 3fe619bf30..5d4fddcabf 100644
--- a/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp
+++ b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp
@@ -1,626 +1,626 @@
/*============================================================================
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 "mitkSegmentationInterpolationController.h"
#include "mitkImageCast.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageTimeSelector.h"
#include <mitkExtractSliceFilter.h>
#include <mitkImageAccessByItk.h>
//#include <mitkPlaneGeometry.h>
#include <itkCommand.h>
#include <itkImage.h>
#include <itkImageSliceConstIteratorWithIndex.h>
#include <thread>
namespace
{
// itk::Object provides a const version of AddObserver() (which uses const_cast internally)
// but not a const version of RemoveObserver().
void RemoveObserverFromConstObject(const itk::Object* constObject, unsigned long observerTag)
{
if (nullptr != constObject)
{
auto* object = const_cast<itk::Object*>(constObject);
object->RemoveObserver(observerTag);
}
}
}
mitk::SegmentationInterpolationController::InterpolatorMapType
mitk::SegmentationInterpolationController::s_InterpolatorForImage; // static member initialization
mitk::SegmentationInterpolationController *mitk::SegmentationInterpolationController::InterpolatorForImage(
const Image *image)
{
auto iter = s_InterpolatorForImage.find(image);
if (iter != s_InterpolatorForImage.end())
{
return iter->second;
}
else
{
return nullptr;
}
}
mitk::SegmentationInterpolationController::SegmentationInterpolationController()
: m_SegmentationModifiedObserverTag(std::make_pair(0UL, false)),
m_BlockModified(false),
m_2DInterpolationActivated(false),
m_EnableSliceImageCache(false)
{
}
void mitk::SegmentationInterpolationController::Activate2DInterpolation(bool status)
{
m_2DInterpolationActivated = status;
}
mitk::SegmentationInterpolationController *mitk::SegmentationInterpolationController::GetInstance()
{
static mitk::SegmentationInterpolationController::Pointer m_Instance;
if (m_Instance.IsNull())
{
m_Instance = SegmentationInterpolationController::New();
}
return m_Instance;
}
mitk::SegmentationInterpolationController::~SegmentationInterpolationController()
{
// remove this from the list of interpolators
for (auto iter = s_InterpolatorForImage.begin(); iter != s_InterpolatorForImage.end(); ++iter)
{
if (iter->second == this)
{
s_InterpolatorForImage.erase(iter);
break;
}
}
}
void mitk::SegmentationInterpolationController::OnImageModified(const itk::EventObject &)
{
if (!m_BlockModified && m_Segmentation.IsNotNull() && m_2DInterpolationActivated)
{
SetSegmentationVolume(m_Segmentation);
}
}
void mitk::SegmentationInterpolationController::BlockModified(bool block)
{
m_BlockModified = block;
}
void mitk::SegmentationInterpolationController::SetSegmentationVolume(const Image *segmentation)
{
// clear old information (remove all time steps
m_SegmentationCountInSlice.clear();
// delete this from the list of interpolators
auto iter = s_InterpolatorForImage.find(segmentation);
if (iter != s_InterpolatorForImage.end())
{
s_InterpolatorForImage.erase(iter);
}
if (m_SegmentationModifiedObserverTag.second)
{
RemoveObserverFromConstObject(m_Segmentation, m_SegmentationModifiedObserverTag.first);
m_SegmentationModifiedObserverTag.second = false;
}
if (nullptr == segmentation || !segmentation->IsInitialized())
{
m_Segmentation = nullptr;
this->InvokeEvent(itk::AbortEvent());
return;
}
if (segmentation->GetDimension() > 4 || segmentation->GetDimension() < 3)
{
itkExceptionMacro("SegmentationInterpolationController needs a 3D-segmentation or 3D+t.");
}
m_Segmentation = segmentation;
auto command = itk::ReceptorMemberCommand<SegmentationInterpolationController>::New();
command->SetCallbackFunction(this, &SegmentationInterpolationController::OnImageModified);
m_SegmentationModifiedObserverTag.first = segmentation->AddObserver(itk::ModifiedEvent(), command);
m_SegmentationModifiedObserverTag.second = true;
m_SegmentationCountInSlice.resize(m_Segmentation->GetTimeSteps());
for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep)
{
m_SegmentationCountInSlice[timeStep].resize(3);
for (unsigned int dim = 0; dim < 3; ++dim)
{
m_SegmentationCountInSlice[timeStep][dim].clear();
m_SegmentationCountInSlice[timeStep][dim].resize(m_Segmentation->GetDimension(dim));
m_SegmentationCountInSlice[timeStep][dim].assign(m_Segmentation->GetDimension(dim), 0);
}
}
s_InterpolatorForImage.insert(std::make_pair(m_Segmentation, this));
// for all timesteps
// scan whole image
for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep)
{
ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(m_Segmentation);
timeSelector->SetTimeNr(timeStep);
timeSelector->UpdateLargestPossibleRegion();
Image::Pointer segmentation3D = timeSelector->GetOutput();
AccessFixedDimensionByItk_2(segmentation3D, ScanWholeVolume, 3, m_Segmentation, timeStep);
}
// PrintStatus();
SetReferenceVolume(m_ReferenceImage);
Modified();
}
void mitk::SegmentationInterpolationController::SetReferenceVolume(const Image *referenceImage)
{
m_ReferenceImage = referenceImage;
if (m_ReferenceImage.IsNull())
return; // no image set - ignore it then
assert(m_Segmentation.IsNotNull()); // should never happen
// ensure the reference image has the same dimensionality and extents as the segmentation image
if (m_ReferenceImage.IsNull() || m_Segmentation.IsNull() ||
m_ReferenceImage->GetDimension() != m_Segmentation->GetDimension() ||
m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 ||
m_Segmentation->GetPixelType().GetNumberOfComponents() != 1)
{
MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl;
m_ReferenceImage = nullptr;
return;
}
for (unsigned int dim = 0; dim < m_Segmentation->GetDimension(); ++dim)
if (m_ReferenceImage->GetDimension(dim) != m_Segmentation->GetDimension(dim))
{
MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim
<< "), ignoring patient image" << std::endl;
m_ReferenceImage = nullptr;
return;
}
}
void mitk::SegmentationInterpolationController::SetChangedVolume(const Image *sliceDiff, unsigned int timeStep)
{
if (!sliceDiff)
return;
if (sliceDiff->GetDimension() != 3)
return;
AccessFixedDimensionByItk_1(sliceDiff, ScanChangedVolume, 3, timeStep);
// PrintStatus();
Modified();
}
void mitk::SegmentationInterpolationController::SetChangedSlice(const Image *sliceDiff,
unsigned int sliceDimension,
unsigned int sliceIndex,
unsigned int timeStep)
{
if (!sliceDiff)
return;
if (sliceDimension > 2)
return;
if (timeStep >= m_SegmentationCountInSlice.size())
return;
if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size())
return;
unsigned int dim0(0);
unsigned int dim1(1);
// determine the other two dimensions
switch (sliceDimension)
{
default:
case 2:
dim0 = 0;
dim1 = 1;
break;
case 1:
dim0 = 0;
dim1 = 2;
break;
case 0:
dim0 = 1;
dim1 = 2;
break;
}
mitk::ImageReadAccessor readAccess(sliceDiff);
auto *rawSlice = (unsigned char *)readAccess.GetData();
if (!rawSlice)
return;
AccessFixedDimensionByItk_1(
sliceDiff, ScanChangedSlice, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep, rawSlice));
Modified();
}
template <typename DATATYPE>
void mitk::SegmentationInterpolationController::ScanChangedSlice(const itk::Image<DATATYPE, 2> *,
const SetChangedSliceOptions &options)
{
auto *pixelData((DATATYPE *)options.pixelData);
unsigned int timeStep(options.timeStep);
unsigned int sliceDimension(options.sliceDimension);
unsigned int sliceIndex(options.sliceIndex);
if (sliceDimension > 2)
return;
if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size())
return;
unsigned int dim0(options.dim0);
unsigned int dim1(options.dim1);
int numberOfPixels(0); // number of pixels in this slice that are not 0
unsigned int dim0max = m_SegmentationCountInSlice[timeStep][dim0].size();
unsigned int dim1max = m_SegmentationCountInSlice[timeStep][dim1].size();
// scan the slice from two directions
// and set the flags for the two dimensions of the slice
for (unsigned int v = 0; v < dim1max; ++v)
{
for (unsigned int u = 0; u < dim0max; ++u)
{
DATATYPE value = *(pixelData + u + v * dim0max);
assert((signed)m_SegmentationCountInSlice[timeStep][dim0][u] + (signed)value >=
0); // just for debugging. This must always be true, otherwise some counting is going wrong
assert((signed)m_SegmentationCountInSlice[timeStep][dim1][v] + (signed)value >= 0);
m_SegmentationCountInSlice[timeStep][dim0][u] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][dim0][u] + value);
m_SegmentationCountInSlice[timeStep][dim1][v] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][dim1][v] + value);
numberOfPixels += static_cast<int>(value);
}
}
// flag for the dimension of the slice itself
assert((signed)m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] + numberOfPixels >= 0);
m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] += numberOfPixels;
// MITK_INFO << "scan t=" << timeStep << " from (0,0) to (" << dim0max << "," << dim1max << ") (" << pixelData << "-"
// << pixelData+dim0max*dim1max-1 << ") in slice " << sliceIndex << " found " << numberOfPixels << " pixels" <<
// std::endl;
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::SegmentationInterpolationController::ScanChangedVolume(const itk::Image<TPixel, VImageDimension> *diffImage,
unsigned int timeStep)
{
typedef itk::ImageSliceConstIteratorWithIndex<itk::Image<TPixel, VImageDimension>> IteratorType;
IteratorType iter(diffImage, diffImage->GetLargestPossibleRegion());
iter.SetFirstDirection(0);
iter.SetSecondDirection(1);
int numberOfPixels(0); // number of pixels in this slice that are not 0
typename IteratorType::IndexType index;
unsigned int x = 0;
unsigned int y = 0;
unsigned int z = 0;
iter.GoToBegin();
while (!iter.IsAtEnd())
{
while (!iter.IsAtEndOfSlice())
{
while (!iter.IsAtEndOfLine())
{
index = iter.GetIndex();
x = index[0];
y = index[1];
z = index[2];
TPixel value = iter.Get();
assert((signed)m_SegmentationCountInSlice[timeStep][0][x] + (signed)value >=
0); // just for debugging. This must always be true, otherwise some counting is going wrong
assert((signed)m_SegmentationCountInSlice[timeStep][1][y] + (signed)value >= 0);
m_SegmentationCountInSlice[timeStep][0][x] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][0][x] + value);
m_SegmentationCountInSlice[timeStep][1][y] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][1][y] + value);
numberOfPixels += static_cast<int>(value);
++iter;
}
iter.NextLine();
}
assert((signed)m_SegmentationCountInSlice[timeStep][2][z] + numberOfPixels >= 0);
m_SegmentationCountInSlice[timeStep][2][z] += numberOfPixels;
numberOfPixels = 0;
iter.NextSlice();
}
}
template <typename DATATYPE>
void mitk::SegmentationInterpolationController::ScanWholeVolume(const itk::Image<DATATYPE, 3> *,
const Image *volume,
unsigned int timeStep)
{
if (!volume)
return;
if (timeStep >= m_SegmentationCountInSlice.size())
return;
ImageReadAccessor readAccess(volume, volume->GetVolumeData(timeStep));
for (unsigned int slice = 0; slice < volume->GetDimension(2); ++slice)
{
const auto *rawVolume =
static_cast<const DATATYPE *>(readAccess.GetData()); // we again promise not to change anything, we'll just count
const DATATYPE *rawSlice = rawVolume + (volume->GetDimension(0) * volume->GetDimension(1) * slice);
ScanChangedSlice<DATATYPE>(nullptr, SetChangedSliceOptions(2, slice, 0, 1, timeStep, rawSlice));
}
}
void mitk::SegmentationInterpolationController::PrintStatus()
{
- unsigned int timeStep(0); // if needed, put a loop over time steps around everyting, but beware, output will be long
+ unsigned int timeStep(0); // if needed, put a loop over time steps around everything, but beware, output will be long
MITK_INFO << "Interpolator status (timestep 0): dimensions " << m_SegmentationCountInSlice[timeStep][0].size() << " "
<< m_SegmentationCountInSlice[timeStep][1].size() << " " << m_SegmentationCountInSlice[timeStep][2].size()
<< std::endl;
MITK_INFO << "Slice 0: " << m_SegmentationCountInSlice[timeStep][2][0] << std::endl;
// row "x"
for (unsigned int index = 0; index < m_SegmentationCountInSlice[timeStep][0].size(); ++index)
{
if (m_SegmentationCountInSlice[timeStep][0][index] > 0)
MITK_INFO << "O";
else
MITK_INFO << ".";
}
MITK_INFO << std::endl;
// rows "y" and "z" (diagonal)
for (unsigned int index = 1; index < m_SegmentationCountInSlice[timeStep][1].size(); ++index)
{
if (m_SegmentationCountInSlice[timeStep][1][index] > 0)
MITK_INFO << "O";
else
MITK_INFO << ".";
if (m_SegmentationCountInSlice[timeStep][2].size() > index) // if we also have a z value here, then print it, too
{
for (unsigned int indent = 1; indent < index; ++indent)
MITK_INFO << " ";
if (m_SegmentationCountInSlice[timeStep][2][index] > 0)
MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O";
else
MITK_INFO << ".";
}
MITK_INFO << std::endl;
}
// z indices that are larger than the biggest y index
for (unsigned int index = m_SegmentationCountInSlice[timeStep][1].size();
index < m_SegmentationCountInSlice[timeStep][2].size();
++index)
{
for (unsigned int indent = 0; indent < index; ++indent)
MITK_INFO << " ";
if (m_SegmentationCountInSlice[timeStep][2][index] > 0)
MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O";
else
MITK_INFO << ".";
MITK_INFO << std::endl;
}
}
mitk::Image::Pointer mitk::SegmentationInterpolationController::Interpolate(unsigned int sliceDimension,
unsigned int sliceIndex,
const mitk::PlaneGeometry *currentPlane,
unsigned int timeStep,
ShapeBasedInterpolationAlgorithm::Pointer algorithm)
{
if (m_Segmentation.IsNull() || nullptr == currentPlane)
return nullptr;
if (timeStep >= m_SegmentationCountInSlice.size())
return nullptr;
if (sliceDimension > 2)
return nullptr;
if (0 == sliceIndex)
return nullptr; // First slice, nothing to interpolate
const unsigned int lastSliceIndex = m_SegmentationCountInSlice[timeStep][sliceDimension].size() - 1;
if (lastSliceIndex <= sliceIndex)
return nullptr; // Last slice, nothing to interpolate
if (m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] > 0)
return nullptr; // Slice contains segmentation, nothing to interopolate
unsigned int lowerBound = 0;
unsigned int upperBound = 0;
bool bounds = false;
for (lowerBound = sliceIndex - 1; ; --lowerBound)
{
if (m_SegmentationCountInSlice[timeStep][sliceDimension][lowerBound] > 0)
{
bounds = true;
break;
}
if (0 == lowerBound)
break;
}
if (!bounds)
return nullptr;
bounds = false;
for (upperBound = sliceIndex + 1; upperBound <= lastSliceIndex; ++upperBound)
{
if (m_SegmentationCountInSlice[timeStep][sliceDimension][upperBound] > 0)
{
bounds = true;
break;
}
}
if (!bounds)
return nullptr;
// We have found two neighboring slices with segmentations and made sure that the current slice does not contain anything
mitk::Image::Pointer lowerSlice;
mitk::Image::Pointer upperSlice;
mitk::Image::Pointer resultImage;
try
{
// Extract current slice
resultImage = this->ExtractSlice(currentPlane, sliceIndex, timeStep);
// Creating PlaneGeometry for lower slice
auto reslicePlane = currentPlane->Clone();
// Transforming the current origin so that it matches the lower slice
auto origin = currentPlane->GetOrigin();
m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin);
origin[sliceDimension] = lowerBound;
m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin);
reslicePlane->SetOrigin(origin);
// Extract lower slice
lowerSlice = this->ExtractSlice(reslicePlane, lowerBound, timeStep, true);
if (lowerSlice.IsNull())
return nullptr;
// Transforming the current origin so that it matches the upper slice
m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin);
origin[sliceDimension] = upperBound;
m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin);
reslicePlane->SetOrigin(origin);
// Extract the upper slice
upperSlice = this->ExtractSlice(reslicePlane, upperBound, timeStep, true);
if (upperSlice.IsNull())
return nullptr;
}
catch (const std::exception &e)
{
MITK_ERROR << "Error in 2D interpolation: " << e.what();
return nullptr;
}
// Interpolation algorithm inputs:
// - Two segmentations (guaranteed to be of the same data type)
// - Orientation of the segmentations (sliceDimension)
// - Position of the two slices (sliceIndices)
// - Reference image
//
// The interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to
// inspect the reference image at appropriate positions.
if (algorithm.IsNull())
algorithm = mitk::ShapeBasedInterpolationAlgorithm::New();
return algorithm->Interpolate(
lowerSlice.GetPointer(),
lowerBound,
upperSlice.GetPointer(),
upperBound,
sliceIndex,
sliceDimension,
resultImage,
timeStep,
m_ReferenceImage);
}
mitk::Image::Pointer mitk::SegmentationInterpolationController::ExtractSlice(const PlaneGeometry* planeGeometry, unsigned int sliceIndex, unsigned int timeStep, bool cache)
{
static const auto MAX_CACHE_SIZE = 2 * std::thread::hardware_concurrency();
const auto key = std::make_pair(sliceIndex, timeStep);
if (cache && m_EnableSliceImageCache)
{
std::lock_guard<std::mutex> lock(m_SliceImageCacheMutex);
if (0 != m_SliceImageCache.count(key))
return m_SliceImageCache[key];
if (MAX_CACHE_SIZE < m_SliceImageCache.size())
m_SliceImageCache.clear();
}
auto extractor = ExtractSliceFilter::New();
extractor->SetInput(m_Segmentation);
extractor->SetTimeStep(timeStep);
extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
extractor->SetVtkOutputRequest(false);
extractor->SetWorldGeometry(planeGeometry);
extractor->Update();
if (cache && m_EnableSliceImageCache)
{
std::lock_guard<std::mutex> lock(m_SliceImageCacheMutex);
m_SliceImageCache[key] = extractor->GetOutput();
}
return extractor->GetOutput();
}
void mitk::SegmentationInterpolationController::EnableSliceImageCache()
{
m_EnableSliceImageCache = true;
}
void mitk::SegmentationInterpolationController::DisableSliceImageCache()
{
m_EnableSliceImageCache = false;
m_SliceImageCache.clear();
}
diff --git a/Modules/Segmentation/Controllers/mitkToolManager.h b/Modules/Segmentation/Controllers/mitkToolManager.h
index e82b5eeaf7..edb52d86b0 100644
--- a/Modules/Segmentation/Controllers/mitkToolManager.h
+++ b/Modules/Segmentation/Controllers/mitkToolManager.h
@@ -1,300 +1,300 @@
/*============================================================================
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 mitkToolManager_h
#define mitkToolManager_h
#include "mitkDataNode.h"
#include "mitkDataStorage.h"
#include "mitkTool.h"
#include "mitkWeakPointer.h"
#include <MitkSegmentationExports.h>
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
#include <vector>
namespace mitk
{
class Image;
class PlaneGeometry;
/**
\brief Manages and coordinates instances of mitk::Tool.
\sa QmitkToolSelectionBox
\sa Tool
\sa QmitkSegmentationView
\ingroup Interaction
\ingroup ToolManagerEtAl
This class creates and manages several instances of mitk::Tool.
\li ToolManager creates instances of mitk::Tool by asking the itk::ObjectFactory to list all known implementations
of mitk::Tool.
As a result, one has to implement both a subclass of mitk::Tool and a matching subclass of
itk::ObjectFactoryBase that is registered
- to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitiation of
+ to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitation of
one-class-one-factory is due
to the implementation of itk::ObjectFactory).
In MITK, the right place to register the factories to itk::ObjectFactory is the mitk::QMCoreObjectFactory or
mitk::SBCoreObjectFactory.
\li ToolManager knows a set of "reference" DataNodes and a set of "working" DataNodes. The first application are
segmentation tools, where the
reference is the original image and the working data the (kind of) binary segmentation. However, ToolManager is
implemented more generally, so that
there could be other tools that work, e.g., with surfaces.
\li There is a set of events that are sent by ToolManager. At the moment these are TODO update documentation:
- mitk::ToolReferenceDataChangedEvent whenever somebody calls SetReferenceData. Most of the time this actually
means that the data has changed, but
there might be cases where the same data is passed to SetReferenceData a second time, so don't rely on the
assumption that something actually changed.
- mitk::ToolSelectedEvent is sent when a (truly) different tool was activated. In reaction to this event you can
ask for the active Tool using
GetActiveTool or GetActiveToolID (where nullptr or -1 indicate that NO tool is active at the moment).
- Design descisions:
+ Design decisions:
\li Not a singleton, because there could be two functionalities using tools, each one with different
reference/working data.
$Author$
*/
class MITKSEGMENTATION_EXPORT ToolManager : public itk::Object
{
public:
typedef std::vector<Tool::Pointer> ToolVectorType;
typedef std::vector<Tool::ConstPointer> ToolVectorTypeConst;
typedef std::vector<DataNode *> DataVectorType; // has to be observed for delete events!
typedef std::map<DataNode *, unsigned long> NodeTagMapType;
Message<> NodePropertiesChanged;
Message<> NewNodesGenerated;
Message1<DataVectorType *> NewNodeObjectsGenerated;
Message<> ActiveToolChanged;
Message<> ReferenceDataChanged;
Message<> WorkingDataChanged;
Message<> RoiDataChanged;
Message<> SelectedTimePointChanged;
Message1<std::string> ToolErrorMessage;
Message1<std::string> GeneralToolMessage;
mitkClassMacroItkParent(ToolManager, itk::Object);
mitkNewMacro1Param(ToolManager, DataStorage *);
/**
\brief Gives you a list of all tools.
This is const on purpose.
*/
const ToolVectorTypeConst GetTools();
int GetToolID(const Tool *tool);
/**
\param id The tool of interest.
Counting starts with 0.
*/
Tool *GetToolById(int id);
/**
\param id The tool to activate. Provide -1 for disabling any tools.
Counting starts with 0.
- Registeres a listner for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved).
+ Registers a listener for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved).
*/
bool ActivateTool(int id);
template <class T>
int GetToolIdByToolType()
{
int id = 0;
for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id)
{
if (dynamic_cast<T *>(iter->GetPointer()))
{
return id;
}
}
return -1;
}
/**
\return -1 for "No tool is active"
*/
int GetActiveToolID();
/**
\return nullptr for "No tool is active"
*/
Tool *GetActiveTool();
/**
\brief Set a list of data/images as reference objects.
*/
void SetReferenceData(DataVectorType);
/**
\brief Set single data item/image as reference object.
*/
void SetReferenceData(DataNode *);
/**
\brief Set a list of data/images as working objects.
*/
void SetWorkingData(DataVectorType);
/**
\brief Set single data item/image as working object.
*/
void SetWorkingData(DataNode *);
/**
\brief Set a list of data/images as roi objects.
*/
void SetRoiData(DataVectorType);
/**
\brief Set a single data item/image as roi object.
*/
void SetRoiData(DataNode *);
/**
\brief Get the list of reference data.
*/
DataVectorType GetReferenceData();
/**
\brief Get the current reference data.
\warning If there is a list of items, this method will only return the first list item.
*/
DataNode *GetReferenceData(int);
/**
\brief Get the list of working data.
*/
DataVectorType GetWorkingData();
/**
\brief Get the current working data.
\warning If there is a list of items, this method will only return the first list item.
*/
DataNode *GetWorkingData(unsigned int);
/**
\brief Get the current roi data
*/
DataVectorType GetRoiData();
/**
\brief Get the roi data at position idx
*/
DataNode *GetRoiData(int idx);
DataStorage::Pointer GetDataStorage() const;
void SetDataStorage(DataStorage &storage);
/** Get the current selected time point of the RenderManager
*/
TimePointType GetCurrentTimePoint() const;
/**
\brief Tell that someone is using tools.
GUI elements should call this when they become active. This method increases an internal "client count".
*/
void RegisterClient();
/**
\brief Tell that someone is NOT using tools.
GUI elements should call this when they become active. This method increases an internal "client count".
*/
void UnregisterClient();
/** \brief Initialize all classes derived from mitk::Tool by itkObjectFactoy */
void InitializeTools();
void OnOneOfTheReferenceDataDeletedConst(const itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheReferenceDataDeleted(itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheWorkingDataDeletedConst(const itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheWorkingDataDeleted(itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheRoiDataDeletedConst(const itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheRoiDataDeleted(itk::Object *caller, const itk::EventObject &e);
/**
\brief Connected to tool's messages
This method just resends error messages coming from any of the tools. This way clients (GUIs) only have to observe
one message.
*/
void OnToolErrorMessage(std::string s);
void OnGeneralToolMessage(std::string s);
protected:
/**
You may specify a list of tool "groups" that should be available for this ToolManager. Every Tool can report its
group
as a string. This constructor will try to find the tool's group inside the supplied string. If there is a match,
the tool is accepted. Effectively, you can provide a human readable list like "default, lymphnodevolumetry,
oldERISstuff".
*/
ToolManager(DataStorage *storage); // purposely hidden
~ToolManager() override;
ToolVectorType m_Tools;
Tool *m_ActiveTool;
int m_ActiveToolID;
us::ServiceRegistration<InteractionEventObserver> m_ActiveToolRegistration;
DataVectorType m_ReferenceData;
NodeTagMapType m_ReferenceDataObserverTags;
DataVectorType m_WorkingData;
NodeTagMapType m_WorkingDataObserverTags;
DataVectorType m_RoiData;
NodeTagMapType m_RoiDataObserverTags;
int m_RegisteredClients;
WeakPointer<DataStorage> m_DataStorage;
/// \brief Callback for NodeRemove events
void OnNodeRemoved(const mitk::DataNode *node);
/** Callback for time changed events*/
void OnTimeChangedConst(const itk::Object* caller, const itk::EventObject& e);
void OnTimeChanged(itk::Object* caller, const itk::EventObject& e);
void EnsureTimeObservation();
void StopTimeObservation();
private:
/** Time point of last detected change*/
TimePointType m_LastTimePoint = 0;
/** Tag of the observer that listens to time changes*/
unsigned long m_TimePointObserverTag = 0;
/** Pointer to the observed time stepper*/
WeakPointer<TimeNavigationController> m_CurrentTimeNavigationController;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp
index 48cfaf8ff7..b07f24e53f 100644
--- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp
+++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp
@@ -1,331 +1,331 @@
/*============================================================================
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 "mitkContourModelLiveWireInteractor.h"
#include "mitkInteractionPositionEvent.h"
#include "mitkToolManager.h"
#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"
#include <mitkInteractionConst.h>
#include "mitkIOUtil.h"
mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor() : ContourModelInteractor()
{
m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New();
m_LiveWireFilter->SetUseCostFunction(true);
m_NextActiveVertexDown.Fill(0);
m_NextActiveVertexUp.Fill(0);
}
mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor()
{
}
void mitk::ContourModelLiveWireInteractor::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick);
CONNECT_CONDITION("mouseMove", IsHovering);
CONNECT_FUNCTION("movePoint", OnMovePoint);
CONNECT_FUNCTION("deletePoint", OnDeletePoint);
CONNECT_FUNCTION("addPoint", OnAddPoint)
CONNECT_FUNCTION("finish", OnFinishEditing);
}
bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent)
{
auto isVertexSelected = Superclass::OnCheckPointClick(interactionEvent);
if (isVertexSelected)
{
auto* contour = dynamic_cast<mitk::ContourModel*>(this->GetDataNode()->GetData());
const auto* positionEvent =
dynamic_cast<const mitk::InteractionPositionEvent*>(interactionEvent);
mitk::Point3D click = positionEvent->GetPositionInWorld();
const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
auto controlVertices = contour->GetControlVertices(timeStep);
const mitk::ContourModel::VertexType* nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
const mitk::ContourModel::VertexType* previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
this->SplitContourFromSelectedVertex(contour, nextPoint, previousPoint, timeStep);
m_NextActiveVertexUp = nextPoint->Coordinates;
m_NextActiveVertexDown = previousPoint->Coordinates;
// clear previous void positions
this->m_LiveWireFilter->ClearRepulsivePoints();
// all points in lower and upper part should be marked as repulsive points to not be changed
this->SetRepulsivePoints(previousPoint, m_ContourLeft, timeStep);
this->SetRepulsivePoints(nextPoint, m_ContourRight, timeStep);
// clear container with void points between neighboring control points
m_ContourBeingModified.clear();
}
return isVertexSelected;
}
void mitk::ContourModelLiveWireInteractor::SetWorkingImage(mitk::Image *_arg)
{
if (this->m_WorkingSlice != _arg)
{
this->m_WorkingSlice = _arg;
this->m_LiveWireFilter->SetInput(this->m_WorkingSlice);
}
}
void mitk::ContourModelLiveWireInteractor::OnAddPoint(StateMachineAction* sm, InteractionEvent* interactionEvent)
{
Superclass::OnAddPoint(sm, interactionEvent);
}
void mitk::ContourModelLiveWireInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
if (contour == nullptr)
{
MITK_ERROR << "Invalid Contour!";
return;
}
if (contour->GetSelectedVertex())
{
mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
newContour->Expand(contour->GetTimeSteps());
newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());
newContour->Concatenate(m_ContourLeft, timeStep);
// recompute contour between neighbored two active control points
this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp);
this->m_LiveWireFilter->Update();
mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput();
assert(liveWireContour);
if (liveWireContour->IsEmpty(timeStep))
return;
liveWireContour->RemoveVertexAt(0, timeStep);
liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timeStep) - 1, timeStep);
// insert new live wire computed points
newContour->Concatenate(liveWireContour, timeStep);
// insert right side of original contour
newContour->Concatenate(this->m_ContourRight, timeStep);
newContour->SetClosed(contour->IsClosed(timeStep), timeStep);
// instead of leaving a single point, delete all points
if (newContour->GetNumberOfVertices(timeStep) <= 2)
{
newContour->Clear(timeStep);
}
this->GetDataNode()->SetData(newContour);
mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow());
}
}
void mitk::ContourModelLiveWireInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (!positionEvent)
return;
const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
mitk::Point3D currentPosition = positionEvent->GetPositionInWorld();
auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
if (contour == nullptr)
{
MITK_ERROR << "invalid contour";
return;
}
mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New();
editingContour->Expand(contour->GetTimeSteps());
editingContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());
// recompute left live wire, i.e. the contour between previous active vertex and selected vertex
this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
this->m_LiveWireFilter->SetEndPoint(currentPosition);
// remove void positions between previous active vertex and next active vertex.
if (!m_ContourBeingModified.empty())
{
std::vector<itk::Index<2>>::const_iterator iter = m_ContourBeingModified.begin();
for (; iter != m_ContourBeingModified.end(); iter++)
{
this->m_LiveWireFilter->RemoveRepulsivePoint((*iter));
}
}
// update to get the left livewire. Remember that the points in the rest of the contour are already
// set as void positions in the filter
this->m_LiveWireFilter->Update();
mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput();
assert(leftLiveWire);
if (!leftLiveWire->IsEmpty(timeStep))
leftLiveWire->RemoveVertexAt(0, timeStep);
editingContour->Concatenate(leftLiveWire, timeStep);
// the new index of the selected vertex
unsigned int selectedVertexIndex =
this->m_ContourLeft->GetNumberOfVertices(timeStep) + leftLiveWire->GetNumberOfVertices(timeStep) - 1;
// at this point the container has to be empty
m_ContourBeingModified.clear();
// add points from left live wire contour
auto iter = leftLiveWire->IteratorBegin(timeStep);
for (; iter != leftLiveWire->IteratorEnd(timeStep); iter++)
{
itk::Index<2> idx;
this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
this->m_LiveWireFilter->AddRepulsivePoint(idx);
// add indices
m_ContourBeingModified.push_back(idx);
}
// recompute right live wire, i.e. the contour between selected vertex and next active vertex
this->m_LiveWireFilter->SetStartPoint(currentPosition);
this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp);
// update filter with all contour points set as void but the right live wire portion to be calculated now
this->m_LiveWireFilter->Update();
mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput();
assert(rightLiveWire);
if (!leftLiveWire->IsEmpty(timeStep))
leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timeStep);
if (!rightLiveWire->IsEmpty(timeStep))
rightLiveWire->RemoveVertexAt(0, timeStep);
editingContour->Concatenate(rightLiveWire, timeStep);
mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
newContour->Expand(contour->GetTimeSteps());
newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());
// concatenate left original contour
newContour->Concatenate(this->m_ContourLeft, timeStep);
newContour->Concatenate(editingContour, timeStep, true);
// set last inserted vertex as selected
newContour->SelectVertexAt(selectedVertexIndex, timeStep);
// set as control point
newContour->SetSelectedVertexAsControlPoint(true);
// concatenate right original contour
newContour->Concatenate(this->m_ContourRight, timeStep);
newContour->SetClosed(contour->IsClosed(timeStep), timeStep);
this->GetDataNode()->SetData(newContour);
mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}
void mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel *srcContour,
const mitk::ContourModel::VertexType *nextPoint,
const mitk::ContourModel::VertexType *previousPoint,
int timeStep)
{
m_ContourLeft = mitk::ContourModel::New();
m_ContourRight = mitk::ContourModel::New();
auto it = srcContour->IteratorBegin();
- // part between nextPoint and end of Countour
+ // part between nextPoint and end of Contour
bool upperPart = false;
- // part between start of countour and previousPoint
+ // part between start of contour and previousPoint
bool lowerPart = true;
// edge cases when point right before first control vertex is selected or first control vertex is selected
if (nextPoint == (*it) || srcContour->GetSelectedVertex() == (*it))
{
upperPart = true;
lowerPart = false;
m_ContourLeft->AddVertex(previousPoint->Coordinates, previousPoint->IsControlPoint, timeStep);
}
// if first control vertex is selected, move to next point before adding vertices to m_ContourRight
// otherwise, second line appears when moving the vertex
if (srcContour->GetSelectedVertex() == (*it))
{
while (*it != nextPoint)
{
it++;
}
}
for (; it != srcContour->IteratorEnd(timeStep); it++)
{
// everything in lower part should be added to m_CountoutLeft
if (lowerPart)
{
m_ContourLeft->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
}
// start of "restricted area" where no vertex should be added to m_CountoutLeft or m_CountoutRight
if (*it == previousPoint)
{
lowerPart = false;
upperPart = false;
}
// start of upperPart
if (*it == nextPoint)
{
upperPart = true;
}
// everything in upper part should be added to m_CountoutRight
if (upperPart)
{
m_ContourRight->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
}
}
}
void mitk::ContourModelLiveWireInteractor::SetRepulsivePoints(const mitk::ContourModel::VertexType *pointToExclude,
mitk::ContourModel *contour,
int timeStep)
{
auto it = contour->IteratorBegin();
for (; it != contour->IteratorEnd(timeStep); it++)
{
if (*it != pointToExclude)
{
itk::Index<2> idx;
this->m_WorkingSlice->GetGeometry()->WorldToIndex((*it)->Coordinates, idx);
this->m_LiveWireFilter->AddRepulsivePoint(idx);
}
}
}
void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *)
{
}
diff --git a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h
index 519f5b361a..d189e21a85 100644
--- a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h
+++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h
@@ -1,141 +1,141 @@
/*============================================================================
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 mitkFeedbackContourTool_h
#define mitkFeedbackContourTool_h
#include "mitkCommon.h"
#include "mitkContourModelUtils.h"
#include "mitkContourUtils.h" //TODO remove legacy support
#include "mitkImage.h"
#include "mitkSegTool2D.h"
#include <MitkSegmentationExports.h>
#include "mitkDataNode.h"
#include "mitkImageCast.h"
namespace mitk
{
/**
\brief Base class for tools that use a contour for feedback
\sa Tool
\sa ContourModel
\ingroup Interaction
\ingroup ToolManagerEtAl
Implements helper methods, that might be of use to all kind of 2D segmentation tools that use a contour for user
feedback.
- Providing a feedback contour that might be added or removed from the visible scene (SetFeedbackContourVisible).
- Filling of a contour into a 2D slice
These helper methods are actually implemented in ContourUtils now. FeedbackContourTool only forwards such requests.
\warning Only to be instantiated by mitk::ToolManager.
$Author: nolden $
*/
class MITKSEGMENTATION_EXPORT FeedbackContourTool : public SegTool2D
{
public:
mitkClassMacro(FeedbackContourTool, SegTool2D);
protected:
FeedbackContourTool(); // purposely hidden
FeedbackContourTool(const char *); // purposely hidden
~FeedbackContourTool() override;
const ContourModel *GetFeedbackContour() const;
/** (Re)initialize the feesback contour by creating a new instance.
* It is assured that the new instance as the same time geometry than
* the working image.*/
void InitializeFeedbackContour(bool isClosed);
/** Clears the current time step of the feedback contour and resets its closed state.*/
void ClearsCurrentFeedbackContour(bool isClosed);
/** Updates the feedback contour of the currently selected time point. The update will be done
* by clearing all existings vertices at the current time point and copying the vertics of the
* source model at the specified source time step.*/
void UpdateCurrentFeedbackContour(const ContourModel* sourceModel, TimeStepType sourceTimeStep = 0);
/** Updates the feedback contour at the time step specified by feedbackTimeStep. The update will be done
* by clearing all existings vertices at feedbackTimeStep and copying the vertics of the
* source model at the specified source time step.*/
void UpdateFeedbackContour(const ContourModel* sourceModel, TimeStepType feedbackTimeStep, TimeStepType sourceTimeStep = 0);
/** Adds a vertex to the feedback contour for the current time point. */
void AddVertexToCurrentFeedbackContour(const Point3D& point);
/** Adds a vertex to the feedback contour for the passed time step. If time step is invalid, nothing will be added.*/
void AddVertexToFeedbackContour(const Point3D& point, TimeStepType feedbackTimeStep);
void SetFeedbackContourVisible(bool);
/// Provide values from 0.0 (black) to 1.0 (full color)
void SetFeedbackContourColor(float r, float g, float b);
void SetFeedbackContourColorDefault();
void Deactivated() override;
void Activated() override;
/**
\brief Projects a contour onto an image point by point. Converts from world to index coordinates.
\param slice
\param contourIn3D
*/
ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice,
const ContourModel *contourIn3D);
/**
\brief Projects a slice index coordinates of a contour back into world coordinates.
\param sliceGeometry
\param contourIn2D
*/
ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry,
const ContourModel *contourIn2D);
- /** Helper methods that checks all precondition and if they are fullfilled does the following:
+ /** Helper methods that checks all precondition and if they are fulfilled does the following:
* 1. Gets the contour of the time point specified by positionEvent.
* 2. Gets the affacted working slice of the time point specified by positionEvent.
- * 3. projects the contour onto the working slice and then fills it with the passed paintingPixelValue (adjusted by the current active lable value)
+ * 3. projects the contour onto the working slice and then fills it with the passed paintingPixelValue (adjusted by the current active label value)
* to the slice.
* 4. writes the slice back into the working image using SegTool2D::WriteBackSegmentationResult().*/
void WriteBackFeedbackContourAsSegmentationResult(const InteractionPositionEvent* positionEvent, int paintingPixelValue, bool setInvisibleAfterSuccess = true);
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
*/
void FillContourInSlice(ContourModel *projectedContour, Image *sliceImage, int paintingPixelValue = 1);
/**
\brief Fill a contour in a 2D slice with a specified pixel value at a given time step.
*/
void FillContourInSlice(ContourModel *projectedContour,
unsigned int timeStep,
Image *sliceImage,
int paintingPixelValue = 1);
private:
ContourModel::Pointer m_FeedbackContour;
DataNode::Pointer m_FeedbackContourNode;
bool m_FeedbackContourVisible;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h
index e2d91d2c9e..5e68e1fdf4 100644
--- a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h
+++ b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h
@@ -1,83 +1,83 @@
/*============================================================================
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 mitkFillRegionBaseTool_h
#define mitkFillRegionBaseTool_h
#include "mitkCommon.h"
#include "mitkContourModelUtils.h"
#include "mitkContourUtils.h" //TODO remove legacy support
#include "mitkImage.h"
#include "mitkSegTool2D.h"
#include <MitkSegmentationExports.h>
#include "mitkDataNode.h"
#include "mitkImageCast.h"
namespace mitk
{
/**
\brief Base class for tools that fill a connected region of a 2D slice
\sa Tool
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
$Author: nolden $
*/
class MITKSEGMENTATION_EXPORT FillRegionBaseTool : public SegTool2D
{
public:
mitkClassMacro(FillRegionBaseTool, SegTool2D);
protected:
FillRegionBaseTool(); // purposely hidden
~FillRegionBaseTool() override;
void ConnectActionsAndFunctions() override;
/// \brief Add a control point and finish current segment.
virtual void OnClick(StateMachineAction*, InteractionEvent* interactionEvent);
/** Function that generates the mask image that indicates which pixels should be filled.
* Caller of this function assumes that all pixels that should be filled have the value 1.
* Pixels that should stay untouched should have the value 0.
* The default implementation marks the connected reagion around seedPoint, that has
* the same pixel value/label like the seedPoint.
- * You may reimplement this function to change the strategy to determin the fill region.
- * @param workingSlice part of the segmentation image that should be used to determin the fill image.
+ * You may reimplement this function to change the strategy to determine the fill region.
+ * @param workingSlice part of the segmentation image that should be used to determine the fill image.
* @param seedPoint The world coordinate position where the user has cliced.
* @param [out] seedLabelValue The function should return the label value that should be assumed
* as clicked on, given the seedPoint.
* @return Return the image maske that indicates which pixels should be filled. Returning
* a null pointer indicates that there is nothing to fill.
*/
virtual Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const;
/** Function that is called by OnClick before the filling is executed. If you want to do special
- * preperation (e.g. change m_FillLabelValue, you can overwrite this function. */
+ * preparation (e.g. change m_FillLabelValue, you can overwrite this function. */
virtual void PrepareFilling(const Image* workingSlice, Point3D seedPoint) = 0;
Label::PixelType m_FillLabelValue = 0;
Label::PixelType m_SeedLabelValue = 0;
MultiLabelSegmentation::MergeStyle m_MergeStyle = MultiLabelSegmentation::MergeStyle::Replace;
private:
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkLassoTool.h b/Modules/Segmentation/Interactions/mitkLassoTool.h
index d86b1d3e92..72a5a0f6c2 100644
--- a/Modules/Segmentation/Interactions/mitkLassoTool.h
+++ b/Modules/Segmentation/Interactions/mitkLassoTool.h
@@ -1,63 +1,63 @@
/*============================================================================
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 mitkLassoTool_h
#define mitkLassoTool_h
#include "mitkEditableContourTool.h"
#include "mitkContourTool.h"
#include "mitkContourModelInteractor.h"
namespace mitk
{
/**
\brief A 2D segmentation tool to draw polygon structures.
The contour between the last point and the current mouse position is
computed by forming a straight line.
The tool always assumes that unconfirmed contours are always defined for the
current time point. So the time step in which the contours will be stored as
segmentations will be determined when the contours got confirmed. Then they
- will be transfered to the slices of the currently selected time step.
+ will be transferred to the slices of the currently selected time step.
\sa SegTool2D
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT LassoTool : public EditableContourTool
{
public:
mitkClassMacro(LassoTool, SegTool2D);
itkFactorylessNewMacro(Self);
us::ModuleResource GetCursorIconResource() const override;
us::ModuleResource GetIconResource() const override;
const char *GetName() const override;
const char **GetXPM() const override;
protected:
LassoTool();
~LassoTool() override;
void ConnectActionsAndFunctions() override;
void FinishTool() override;
private:
mitk::ContourModelInteractor::Pointer m_ContourInteractor;
};
}
#endif
diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h
index a598e8c3bb..859fb6e9e9 100644
--- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h
+++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h
@@ -1,90 +1,90 @@
/*============================================================================
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 mitkLiveWireTool2D_h
#define mitkLiveWireTool2D_h
#include <mitkEditableContourTool.h>
#include <mitkContourModelLiveWireInteractor.h>
namespace mitk
{
/**
\brief A 2D segmentation tool based on a LiveWire approach.
The contour between the last point and the current mouse position is
computed by searching the shortest path according to specific features of
the image. The contour thus tends to snap to the boundary of objects.
The tool always assumes that unconfirmed contours are always defined for the
current time point. So the time step in which the contours will be stored as
segmentations will be determined when the contours got confirmed. Then they
- will be transfered to the slices of the currently selected time step.
+ will be transferred to the slices of the currently selected time step.
Changing the time point/time step while tool is active will updated the working
slice the live wire filter. So the behavior of the active live wire contour is
always WYSIWYG (What you see is what you get).
\sa SegTool2D
\sa ImageLiveWireContourModelFilter
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT LiveWireTool2D : public EditableContourTool
{
public:
mitkClassMacro(LiveWireTool2D, EditableContourTool);
itkFactorylessNewMacro(Self);
us::ModuleResource GetCursorIconResource() const override;
us::ModuleResource GetIconResource() const override;
const char *GetName() const override;
const char **GetXPM() const override;
protected:
LiveWireTool2D();
~LiveWireTool2D() override;
void ConnectActionsAndFunctions() override;
void UpdateLiveWireContour();
void OnTimePointChanged() override;
mitk::Point3D PrepareInitContour(const mitk::Point3D& clickedPoint) override;
virtual void FinalizePreviewContour(const Point3D& clickedPoint) override;
virtual void InitializePreviewContour(const Point3D& clickedPoint) override;
virtual void UpdatePreviewContour(const Point3D& clickedPoint) override;
private:
/// \brief Don't use dynamic cost map for LiveWire calculation.
void OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent);
/// \brief Finish contour interaction.
void FinishTool() override;
template <typename TPixel, unsigned int VImageDimension>
void FindHighestGradientMagnitudeByITK(itk::Image<TPixel, VImageDimension> *inputImage,
itk::Index<3> &index,
itk::Index<3> &returnIndex);
mitk::ContourModelLiveWireInteractor::Pointer m_ContourInteractor;
mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter;
bool m_CreateAndUseDynamicCosts;
};
}
#endif
diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
index b45bcd9fde..0709830eb9 100644
--- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
@@ -1,616 +1,616 @@
/*============================================================================
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 "mitkPaintbrushTool.h"
#include "mitkAbstractTransformGeometry.h"
#include "mitkBaseRenderer.h"
#include "mitkToolManager.h"
#include "mitkContourModelUtils.h"
#include "mitkLevelWindowProperty.h"
#include "mitkImageWriteAccessor.h"
int mitk::PaintbrushTool::m_Size = 10;
mitk::PaintbrushTool::PaintbrushTool(bool startWithFillMode)
: FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"),
m_FillMode(startWithFillMode),
m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28)
{
m_MasterContour = ContourModel::New();
m_MasterContour->Initialize();
m_CurrentPlane = nullptr;
}
mitk::PaintbrushTool::~PaintbrushTool()
{
}
void mitk::PaintbrushTool::ConnectActionsAndFunctions()
{
CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed);
CONNECT_FUNCTION("Move", OnPrimaryButtonPressedMoved);
CONNECT_FUNCTION("MouseMove", OnMouseMoved);
CONNECT_FUNCTION("Release", OnMouseReleased);
CONNECT_FUNCTION("InvertLogic", OnInvertLogic);
}
void mitk::PaintbrushTool::Activated()
{
Superclass::Activated();
SizeChanged.Send(m_Size);
this->GetToolManager()->WorkingDataChanged +=
mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
m_PaintingNode = DataNode::New();
m_PaintingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, m_InternalFillValue)));
m_PaintingNode->SetProperty("binary", mitk::BoolProperty::New(true));
m_PaintingNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
m_PaintingNode->SetProperty("name", mitk::StringProperty::New("Paintbrush_Node"));
m_PaintingNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_PaintingNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
m_PaintingNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
auto allRenderWindows = BaseRenderer::GetAll3DRenderWindows();
for (auto mapit = allRenderWindows.begin(); mapit != allRenderWindows.end(); ++mapit)
{
m_PaintingNode->SetVisibility(false, mapit->second);
}
this->UpdateFeedbackColor();
FeedbackContourTool::SetFeedbackContourVisible(true);
this->GetToolManager()->GetDataStorage()->Add(m_PaintingNode);
}
void mitk::PaintbrushTool::Deactivated()
{
FeedbackContourTool::SetFeedbackContourVisible(false);
if (this->GetToolManager()->GetDataStorage()->Exists(m_PaintingNode))
this->GetToolManager()->GetDataStorage()->Remove(m_PaintingNode);
m_WorkingSlice = nullptr;
m_PaintingSlice = nullptr;
m_CurrentPlane = nullptr;
m_PaintingNode = nullptr;
this->GetToolManager()->WorkingDataChanged -=
mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
Superclass::Deactivated();
}
void mitk::PaintbrushTool::SetSize(int value)
{
m_Size = value;
}
mitk::Point2D mitk::PaintbrushTool::upperLeft(mitk::Point2D p)
{
p[0] -= 0.5;
p[1] += 0.5;
return p;
}
void mitk::PaintbrushTool::UpdateContour(const InteractionPositionEvent *positionEvent)
{
// MITK_INFO<<"Update...";
// examine stateEvent and create a contour that matches the pixel mask that we are going to draw
// mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
// const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
if (!positionEvent)
return;
// Get Spacing of current Slice
// mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing();
//
// Draw a contour in Square according to selected brush size
//
int radius = (m_Size) / 2;
float fradius = static_cast<float>(m_Size) / 2.0f;
ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
// estimate center point of the brush ( relative to the pixel the mouse points on )
// -- left upper corner for even sizes,
// -- midpoint for uneven sizes
mitk::Point2D centerCorrection;
centerCorrection.Fill(0);
// even --> correction of [+0.5, +0.5]
bool evenSize = ((m_Size % 2) == 0);
if (evenSize)
{
centerCorrection[0] += 0.5;
centerCorrection[1] += 0.5;
}
// we will compute the control points for the upper left quarter part of a circle contour
std::vector<mitk::Point2D> quarterCycleUpperRight;
std::vector<mitk::Point2D> quarterCycleLowerRight;
std::vector<mitk::Point2D> quarterCycleLowerLeft;
std::vector<mitk::Point2D> quarterCycleUpperLeft;
mitk::Point2D curPoint;
bool curPointIsInside = true;
curPoint[0] = 0;
curPoint[1] = radius;
quarterCycleUpperRight.push_back(upperLeft(curPoint));
// to estimate if a pixel is inside the circle, we need to compare against the 'outer radius'
// i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius]
// const float outer_radius = static_cast<float>(radius) + 0.5;
while (curPoint[1] > 0)
{
// Move right until pixel is outside circle
float curPointX_squared = 0.0f;
float curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]);
while (curPointIsInside)
{
// increment posX and chec
curPoint[0]++;
curPointX_squared = (curPoint[0] - centerCorrection[0]) * (curPoint[0] - centerCorrection[0]);
const float len = sqrt(curPointX_squared + curPointY_squared);
if (len > fradius)
{
// found first Pixel in this horizontal line, that is outside the circle
curPointIsInside = false;
}
}
quarterCycleUpperRight.push_back(upperLeft(curPoint));
// Move down until pixel is inside circle
while (!curPointIsInside)
{
// increment posX and chec
curPoint[1]--;
curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]);
const float len = sqrt(curPointX_squared + curPointY_squared);
if (len <= fradius)
{
// found first Pixel in this horizontal line, that is outside the circle
curPointIsInside = true;
quarterCycleUpperRight.push_back(upperLeft(curPoint));
}
// Quarter cycle is full, when curPoint y position is 0
if (curPoint[1] <= 0)
break;
}
}
// QuarterCycle is full! Now copy quarter cycle to other quarters.
if (!evenSize)
{
std::vector<mitk::Point2D>::const_iterator it = quarterCycleUpperRight.begin();
while (it != quarterCycleUpperRight.end())
{
mitk::Point2D p;
p = *it;
// the contour points in the lower right corner have same position but with negative y values
p[1] *= -1;
quarterCycleLowerRight.push_back(p);
// the contour points in the lower left corner have same position
// but with both x,y negative
p[0] *= -1;
quarterCycleLowerLeft.push_back(p);
// the contour points in the upper left corner have same position
// but with x negative
p[1] *= -1;
quarterCycleUpperLeft.push_back(p);
it++;
}
}
else
{
std::vector<mitk::Point2D>::const_iterator it = quarterCycleUpperRight.begin();
while (it != quarterCycleUpperRight.end())
{
mitk::Point2D p, q;
p = *it;
q = p;
// the contour points in the lower right corner have same position but with negative y values
q[1] *= -1;
// correct for moved offset if size even = the midpoint is not the midpoint of the current pixel
- // but its upper rigt corner
+ // but its upper right corner
q[1] += 1;
quarterCycleLowerRight.push_back(q);
q = p;
// the contour points in the lower left corner have same position
// but with both x,y negative
q[1] = -1.0f * q[1] + 1;
q[0] = -1.0f * q[0] + 1;
quarterCycleLowerLeft.push_back(q);
// the contour points in the upper left corner have same position
// but with x negative
q = p;
q[0] *= -1;
q[0] += 1;
quarterCycleUpperLeft.push_back(q);
it++;
}
}
- // fill contour with poins in right ordering, starting with the upperRight block
+ // fill contour with points in right ordering, starting with the upperRight block
mitk::Point3D tempPoint;
for (unsigned int i = 0; i < quarterCycleUpperRight.size(); i++)
{
tempPoint[0] = quarterCycleUpperRight[i][0];
tempPoint[1] = quarterCycleUpperRight[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
// the lower right has to be parsed in reverse order
for (int i = quarterCycleLowerRight.size() - 1; i >= 0; i--)
{
tempPoint[0] = quarterCycleLowerRight[i][0];
tempPoint[1] = quarterCycleLowerRight[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
for (unsigned int i = 0; i < quarterCycleLowerLeft.size(); i++)
{
tempPoint[0] = quarterCycleLowerLeft[i][0];
tempPoint[1] = quarterCycleLowerLeft[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
// the upper left also has to be parsed in reverse order
for (int i = quarterCycleUpperLeft.size() - 1; i >= 0; i--)
{
tempPoint[0] = quarterCycleUpperLeft[i][0];
tempPoint[1] = quarterCycleUpperLeft[i][1];
tempPoint[2] = 0;
contourInImageIndexCoordinates->AddVertex(tempPoint);
}
m_MasterContour = contourInImageIndexCoordinates;
}
void mitk::PaintbrushTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent)
{
if (m_WorkingSlice.IsNull())
return;
auto* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);
if (!positionEvent)
return;
this->ResetWorkingSlice(positionEvent);
m_WorkingSlice->GetGeometry()->WorldToIndex(positionEvent->GetPositionInWorld(), m_LastPosition);
this->m_PaintingNode->SetVisibility(true);
m_LastEventSender = positionEvent->GetSender();
m_LastEventSlice = m_LastEventSender->GetSlice();
m_MasterContour->SetClosed(true);
this->MouseMoved(interactionEvent, true);
}
void mitk::PaintbrushTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent)
{
MouseMoved(interactionEvent, false);
}
void mitk::PaintbrushTool::OnPrimaryButtonPressedMoved(StateMachineAction *, InteractionEvent *interactionEvent)
{
MouseMoved(interactionEvent, true);
}
/**
Insert the point to the feedback contour,finish to build the contour and at the same time the painting function
*/
void mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent *interactionEvent, bool leftMouseButtonPressed)
{
auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
bool newSlice = CheckIfCurrentSliceHasChanged(positionEvent);
if (newSlice)
{
this->ResetWorkingSlice(positionEvent);
}
if (m_LastContourSize != m_Size)
{
UpdateContour(positionEvent);
m_LastContourSize = m_Size;
}
Point3D worldCoordinates = positionEvent->GetPositionInWorld();
Point3D indexCoordinates;
m_WorkingSlice->GetGeometry()->WorldToIndex(worldCoordinates, indexCoordinates);
// round to nearest voxel center (abort if this hasn't changed)
if (m_Size % 2 == 0) // even
{
indexCoordinates[0] = std::round(indexCoordinates[0]);
indexCoordinates[1] = std::round(indexCoordinates[1]);
}
else // odd
{
indexCoordinates[0] = std::round(indexCoordinates[0]);
indexCoordinates[1] = std::round(indexCoordinates[1]);
}
static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me
if (fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps ||
fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed)
{
lastPos = indexCoordinates;
}
else
{
return;
}
auto contour = ContourModel::New();
contour->SetClosed(true);
auto it = m_MasterContour->Begin();
auto end = m_MasterContour->End();
while (it != end)
{
auto point = (*it)->Coordinates;
point[0] += indexCoordinates[0];
point[1] += indexCoordinates[1];
contour->AddVertex(point);
++it;
}
if (leftMouseButtonPressed)
{
ContourModelUtils::FillContourInSlice2(contour, m_PaintingSlice, m_InternalFillValue);
const double dist = indexCoordinates.EuclideanDistanceTo(m_LastPosition);
const double radius = static_cast<double>(m_Size) / 2.0;
// if points are >= radius away draw rectangle to fill empty holes
// in between the 2 points
if (dist > radius)
{
const mitk::Point3D &currentPos = indexCoordinates;
mitk::Point3D direction;
mitk::Point3D vertex;
mitk::Point3D normal;
direction[0] = indexCoordinates[0] - m_LastPosition[0];
direction[1] = indexCoordinates[1] - m_LastPosition[1];
direction[2] = indexCoordinates[2] - m_LastPosition[2];
direction[0] = direction.GetVnlVector().normalize()[0];
direction[1] = direction.GetVnlVector().normalize()[1];
direction[2] = direction.GetVnlVector().normalize()[2];
// 90 degrees rotation of direction
normal[0] = -1.0 * direction[1];
normal[1] = direction[0];
auto gapContour = ContourModel::New();
// upper left corner
vertex[0] = m_LastPosition[0] + (normal[0] * radius);
vertex[1] = m_LastPosition[1] + (normal[1] * radius);
gapContour->AddVertex(vertex);
// upper right corner
vertex[0] = currentPos[0] + (normal[0] * radius);
vertex[1] = currentPos[1] + (normal[1] * radius);
gapContour->AddVertex(vertex);
// lower right corner
vertex[0] = currentPos[0] - (normal[0] * radius);
vertex[1] = currentPos[1] - (normal[1] * radius);
gapContour->AddVertex(vertex);
// lower left corner
vertex[0] = m_LastPosition[0] - (normal[0] * radius);
vertex[1] = m_LastPosition[1] - (normal[1] * radius);
gapContour->AddVertex(vertex);
ContourModelUtils::FillContourInSlice2(gapContour, m_PaintingSlice, m_InternalFillValue);
}
}
else
{
// switched from different renderwindow
// no activate hover highlighting. Otherwise undo / redo wont work
this->m_PaintingNode->SetVisibility(false);
}
m_LastPosition = indexCoordinates;
// visualize contour
ContourModel::Pointer tmp =
FeedbackContourTool::BackProjectContourFrom2DSlice(m_WorkingSlice->GetGeometry(), contour);
this->UpdateCurrentFeedbackContour(tmp);
if (newSlice)
{
RenderingManager::GetInstance()->RequestUpdateAll();
}
else
{
assert(positionEvent->GetSender()->GetRenderWindow());
RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}
}
void mitk::PaintbrushTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent)
{
// When mouse is released write segmentationresult back into image
auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
if (!positionEvent)
return;
DataNode* workingNode(this->GetToolManager()->GetWorkingData(0));
auto workingImage = dynamic_cast<LabelSetImage*>(workingNode->GetData());
Label::PixelType activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage);
if (!m_FillMode)
{
activePixelValue = LabelSetImage::UNLABELED_VALUE;
}
//as paintbrush tools should always allow to manipulate active label
//(that is what the user expects/knows when using tools so far:
//the active label can always be changed even if locked)
//we realize that by cloning the relevant label and changing the lock state
//this fillLabelSet is used for the transfer.
auto destinationLabels = workingImage->GetConstLabelsByValue(workingImage->GetLabelValuesByGroup(workingImage->GetActiveLayer()));
auto activeLabelClone = workingImage->GetActiveLabel()->Clone();
if (nullptr != activeLabelClone)
{
activeLabelClone->SetLocked(false);
auto activeIter = std::find(destinationLabels.begin(), destinationLabels.end(), workingImage->GetActiveLabel());
if (activeIter == destinationLabels.end()) mitkThrow() << "Application is in an invalid state. Active label is not contained in the labelset, but its group was requested.";
*activeIter = activeLabelClone;
}
TransferLabelContentAtTimeStep(m_PaintingSlice, m_WorkingSlice, destinationLabels, 0, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, { {m_InternalFillValue, activePixelValue} }, mitk::MultiLabelSegmentation::MergeStyle::Merge);
this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone());
// deactivate visibility of helper node
m_PaintingNode->SetVisibility(false);
m_PaintingNode->SetData(nullptr);
m_PaintingSlice = nullptr;
m_WorkingSlice = nullptr;
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PaintbrushTool::UpdateFeedbackColor()
{
mitk::Color currentColor;
if (m_FillMode)
{
FeedbackContourTool::SetFeedbackContourColorDefault();
currentColor.Set(0.0, 1.0, 0.);
}
else
{
FeedbackContourTool::SetFeedbackContourColor(1.0, 0.0, 0.0);
currentColor.Set(1.0, 0.0, 0.);
}
if (m_PaintingNode.IsNotNull())
{
m_PaintingNode->SetProperty("color", mitk::ColorProperty::New(currentColor[0], currentColor[1], currentColor[2]));
}
}
/**
Called when the CTRL key is pressed.
*/
void mitk::PaintbrushTool::OnInvertLogic(StateMachineAction *, InteractionEvent *)
{
m_FillMode = !m_FillMode;
UpdateFeedbackColor();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
bool mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const InteractionPositionEvent *event)
{
const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
const auto* abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry *>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
{
return false;
}
bool newPlane = false;
if (m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull()
//or not the same slice
|| !mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),
m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix())
|| !mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),
m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()))
{
m_CurrentPlane = planeGeometry;
newPlane = true;
}
return newPlane;
}
void mitk::PaintbrushTool::ResetWorkingSlice(const InteractionPositionEvent* event)
{
const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
const auto* abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry*>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
{
return;
}
m_WorkingSlice = nullptr;
m_PaintingSlice = nullptr;
m_PaintingNode->SetData(nullptr);
DataNode* workingNode = this->GetToolManager()->GetWorkingData(0);
if (nullptr == workingNode)
{
return;
}
Image::Pointer image = dynamic_cast<Image*>(workingNode->GetData());
if (nullptr == image)
{
return;
}
m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone();
m_PaintingSlice = Image::New();
m_PaintingSlice->Initialize(m_WorkingSlice);
unsigned int byteSize = m_PaintingSlice->GetPixelType().GetSize();
for (unsigned int dim = 0; dim < m_PaintingSlice->GetDimension(); ++dim)
{
byteSize *= m_PaintingSlice->GetDimension(dim);
}
mitk::ImageWriteAccessor writeAccess(m_PaintingSlice.GetPointer(), m_PaintingSlice->GetVolumeData(0));
memset(writeAccess.GetData(), 0, byteSize);
m_PaintingNode->SetData(m_PaintingSlice);
}
void mitk::PaintbrushTool::OnToolManagerWorkingDataModified()
{
// Here we simply set the current working slice to null. The next time the mouse is moved
// within a renderwindow a new slice will be extracted from the new working data
m_WorkingSlice = nullptr;
m_PaintingSlice = nullptr;
}
diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp
index d0063c907d..5bf3d97430 100644
--- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp
+++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp
@@ -1,795 +1,795 @@
/*============================================================================
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 "mitkSegTool2D.h"
#include "mitkToolManager.h"
#include "mitkBaseRenderer.h"
#include "mitkDataStorage.h"
#include "mitkPlaneGeometry.h"
#include <mitkTimeNavigationController.h>
#include "mitkImageAccessByItk.h"
// Include of the new ImageExtractor
#include "mitkMorphologicalOperations.h"
#include "mitkPlanarCircle.h"
#include "usGetModuleContext.h"
// Includes for 3DSurfaceInterpolation
#include "mitkImageTimeSelector.h"
#include "mitkImageToContourFilter.h"
#include "mitkSurfaceInterpolationController.h"
// includes for resling and overwriting
#include <mitkExtractSliceFilter.h>
#include <mitkVtkImageOverwrite.h>
#include <vtkImageData.h>
#include <vtkSmartPointer.h>
#include "mitkOperationEvent.h"
#include "mitkUndoController.h"
#include <mitkDiffSliceOperationApplier.h>
#include "mitkAbstractTransformGeometry.h"
#include "mitkLabelSetImage.h"
#include "mitkContourModelUtils.h"
// #include <itkImageRegionIterator.h>
#include <vtkAbstractArray.h>
#include <vtkFieldData.h>
#define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a)))
bool mitk::SegTool2D::m_SurfaceInterpolationEnabled = true;
mitk::SegTool2D::SliceInformation::SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep) :
slice(aSlice), plane(aPlane), timestep(aTimestep)
{
}
mitk::SegTool2D::SegTool2D(const char *type, const us::Module *interactorModule)
: Tool(type, interactorModule), m_Contourmarkername("Position")
{
Tool::m_EventConfig = "DisplayConfigBlockLMB.xml";
}
mitk::SegTool2D::~SegTool2D()
{
}
bool mitk::SegTool2D::FilterEvents(InteractionEvent *interactionEvent, DataNode *)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
bool isValidEvent =
(positionEvent && // Only events of type mitk::InteractionPositionEvent
interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D // Only events from the 2D renderwindows
);
return isValidEvent;
}
bool mitk::SegTool2D::DetermineAffectedImageSlice(const Image *image,
const PlaneGeometry *plane,
int &affectedDimension,
int &affectedSlice)
{
assert(image);
assert(plane);
// compare normal of plane to the three axis vectors of the image
Vector3D normal = plane->GetNormal();
Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0);
Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1);
Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2);
normal.Normalize();
imageNormal0.Normalize();
imageNormal1.Normalize();
imageNormal2.Normalize();
imageNormal0.SetVnlVector(vnl_cross_3d<ScalarType>(normal.GetVnlVector(), imageNormal0.GetVnlVector()));
imageNormal1.SetVnlVector(vnl_cross_3d<ScalarType>(normal.GetVnlVector(), imageNormal1.GetVnlVector()));
imageNormal2.SetVnlVector(vnl_cross_3d<ScalarType>(normal.GetVnlVector(), imageNormal2.GetVnlVector()));
double eps(0.00001);
// axial
if (imageNormal2.GetNorm() <= eps)
{
affectedDimension = 2;
}
// sagittal
else if (imageNormal1.GetNorm() <= eps)
{
affectedDimension = 1;
}
// coronal
else if (imageNormal0.GetNorm() <= eps)
{
affectedDimension = 0;
}
else
{
affectedDimension = -1; // no idea
return false;
}
// determine slice number in image
BaseGeometry *imageGeometry = image->GetGeometry(0);
Point3D testPoint = imageGeometry->GetCenter();
Point3D projectedPoint;
plane->Project(testPoint, projectedPoint);
Point3D indexPoint;
imageGeometry->WorldToIndex(projectedPoint, indexPoint);
affectedSlice = ROUND(indexPoint[affectedDimension]);
MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice "
<< affectedSlice;
// check if this index is still within the image
if (affectedSlice < 0 || affectedSlice >= static_cast<int>(image->GetDimension(affectedDimension)))
return false;
return true;
}
void mitk::SegTool2D::UpdateAllSurfaceInterpolations(const LabelSetImage *workingImage,
TimeStepType timeStep,
const PlaneGeometry *plane,
bool detectIntersection)
{
if (nullptr == workingImage) mitkThrow() << "Cannot update surface interpolation. Invalid working image passed.";
if (nullptr == plane) mitkThrow() << "Cannot update surface interpolation. Invalid plane passed.";
auto affectedLabels = mitk::SurfaceInterpolationController::GetInstance()->GetAffectedLabels(workingImage, timeStep, plane);
for (auto affectedLabel : affectedLabels)
{
auto groupID = workingImage->GetGroupIndexOfLabel(affectedLabel);
auto slice = GetAffectedImageSliceAs2DImage(plane, workingImage->GetGroupImage(groupID), timeStep);
std::vector<SliceInformation> slices = { SliceInformation(slice, plane, timeStep) };
Self::UpdateSurfaceInterpolation(slices, workingImage, detectIntersection, affectedLabel, true);
}
if(!affectedLabels.empty()) mitk::SurfaceInterpolationController::GetInstance()->Modified();
}
void mitk::SegTool2D::RemoveContourFromInterpolator(const SliceInformation& sliceInfo, LabelSetImage::LabelValueType labelValue)
{
mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo;
contourInfo.LabelValue = labelValue;
contourInfo.TimeStep = sliceInfo.timestep;
contourInfo.Plane = sliceInfo.plane;
mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo, true);
}
template <typename ImageType>
void ClearBufferProcessing(ImageType* itkImage)
{
itkImage->FillBuffer(0);
}
void mitk::SegTool2D::UpdateSurfaceInterpolation(const std::vector<SliceInformation>& sliceInfos,
const Image* workingImage,
bool detectIntersection,
mitk::Label::PixelType activeLabelValue, bool silent)
{
if (!m_SurfaceInterpolationEnabled)
return;
//Remark: the ImageTimeSelector is just needed to extract a timestep/channel of
//the image in order to get the image dimension (time dimension and channel dimension
//stripped away). Therfore it is OK to always use time step 0 and channel 0
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(workingImage);
timeSelector->SetTimeNr(0);
timeSelector->SetChannelNr(0);
timeSelector->Update();
const auto dimRefImg = timeSelector->GetOutput()->GetDimension();
if (dimRefImg != 3)
return;
std::vector<mitk::Surface::Pointer> contourList;
contourList.reserve(sliceInfos.size());
ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New();
std::vector<SliceInformation> relevantSlices = sliceInfos;
if (detectIntersection)
{
relevantSlices.clear();
for (const auto& sliceInfo : sliceInfos)
{
// Test whether there is something to extract or whether the slice just contains intersections of others
//Remark we cannot just errode the clone of sliceInfo.slice, because Erode currently only
//works on pixel value 1. But we need to erode active label. Therefore we use TransferLabelContent
//as workarround.
//If MorphologicalOperations::Erode is supports user defined pixel values, the workarround
//can be removed.
//Workarround starts
mitk::Image::Pointer slice2 = Image::New();
slice2->Initialize(sliceInfo.slice);
AccessByItk(slice2, ClearBufferProcessing);
LabelSetImage::LabelValueType erodeValue = 1;
auto label = Label::New(erodeValue, "");
TransferLabelContent(sliceInfo.slice, slice2, { label }, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, { {activeLabelValue, erodeValue} });
//Workarround ends
mitk::MorphologicalOperations::Erode(slice2, 2, mitk::MorphologicalOperations::Ball);
contourExtractor->SetInput(slice2);
contourExtractor->SetContourValue(erodeValue);
contourExtractor->Update();
mitk::Surface::Pointer contour = contourExtractor->GetOutput();
if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
{
Self::RemoveContourFromInterpolator(sliceInfo, activeLabelValue);
}
else
{
relevantSlices.push_back(sliceInfo);
}
}
}
SurfaceInterpolationController::CPIVector cpis;
for (const auto& sliceInfo : relevantSlices)
{
contourExtractor->SetInput(sliceInfo.slice);
contourExtractor->SetContourValue(activeLabelValue);
contourExtractor->Update();
mitk::Surface::Pointer contour = contourExtractor->GetOutput();
if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
{
Self::RemoveContourFromInterpolator(sliceInfo, activeLabelValue);
}
else
{
cpis.emplace_back(contour, sliceInfo.plane->Clone(), activeLabelValue, sliceInfo.timestep);
}
}
//this call is relevant even if cpis is empty to ensure SurfaceInterpolationController::Modified is triggered if silent==false;
mitk::SurfaceInterpolationController::GetInstance()->AddNewContours(cpis, false, silent);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component /*= 0*/)
{
if (!positionEvent)
{
return nullptr;
}
assert(positionEvent->GetSender()); // sure, right?
const auto timeStep = positionEvent->GetSender()->GetTimeStep(image); // get the timestep of the visible part (time-wise) of the image
return GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep, component);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, const Image* image, TimePointType timePoint, unsigned int component /*= 0*/)
{
if (!image || !planeGeometry)
{
return nullptr;
}
if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint))
return nullptr;
return SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint), component);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, TimeStepType timeStep, unsigned int component /*= 0*/)
{
if (!image || !planeGeometry)
{
return nullptr;
}
- // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer
+ // Make sure that for reslicing and overwriting the same algorithm is used. We can specify the mode of the vtk reslicer
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// set to false to extract a slice
reslice->SetOverwriteMode(false);
reslice->Modified();
// use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(image);
extractor->SetTimeStep(timeStep);
extractor->SetWorldGeometry(planeGeometry);
extractor->SetVtkOutputRequest(false);
extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
// additionally extract the given component
// default is 0; the extractor checks for multi-component images
extractor->SetComponent(component);
extractor->Modified();
extractor->Update();
Image::Pointer slice = extractor->GetOutput();
return slice;
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent *positionEvent) const
{
const auto workingNode = this->GetWorkingDataNode();
if (!workingNode)
{
return nullptr;
}
const auto *workingImage = dynamic_cast<Image *>(workingNode->GetData());
if (!workingImage)
{
return nullptr;
}
return GetAffectedImageSliceAs2DImage(positionEvent, workingImage);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent *positionEvent) const
{
DataNode* referenceNode = this->GetReferenceDataNode();
if (!referenceNode)
{
return nullptr;
}
auto *referenceImage = dynamic_cast<Image *>(referenceNode->GetData());
if (!referenceImage)
{
return nullptr;
}
int displayedComponent = 0;
if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent))
{
// found the displayed component
return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage, displayedComponent);
}
else
{
return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage);
}
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const
{
DataNode* referenceNode = this->GetReferenceDataNode();
if (!referenceNode)
{
return nullptr;
}
auto* referenceImage = dynamic_cast<Image*>(referenceNode->GetData());
if (!referenceImage)
{
return nullptr;
}
int displayedComponent = 0;
if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent))
{
// found the displayed component
return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep, displayedComponent);
}
else
{
return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep);
}
}
void mitk::SegTool2D::Activated()
{
Superclass::Activated();
this->GetToolManager()->SelectedTimePointChanged +=
mitk::MessageDelegate<mitk::SegTool2D>(this, &mitk::SegTool2D::OnTimePointChangedInternal);
m_LastTimePointTriggered = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
}
void mitk::SegTool2D::Deactivated()
{
this->GetToolManager()->SelectedTimePointChanged -=
mitk::MessageDelegate<mitk::SegTool2D>(this, &mitk::SegTool2D::OnTimePointChangedInternal);
Superclass::Deactivated();
}
void mitk::SegTool2D::OnTimePointChangedInternal()
{
if (m_IsTimePointChangeAware && nullptr != this->GetWorkingDataNode())
{
const TimePointType timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
if (timePoint != m_LastTimePointTriggered)
{
m_LastTimePointTriggered = timePoint;
this->OnTimePointChanged();
}
}
}
void mitk::SegTool2D::OnTimePointChanged()
{
//default implementation does nothing
}
mitk::DataNode* mitk::SegTool2D::GetWorkingDataNode() const
{
if (nullptr != this->GetToolManager())
{
return this->GetToolManager()->GetWorkingData(0);
}
return nullptr;
}
mitk::Image* mitk::SegTool2D::GetWorkingData() const
{
auto node = this->GetWorkingDataNode();
if (nullptr != node)
{
return dynamic_cast<Image*>(node->GetData());
}
return nullptr;
}
mitk::DataNode* mitk::SegTool2D::GetReferenceDataNode() const
{
if (nullptr != this->GetToolManager())
{
return this->GetToolManager()->GetReferenceData(0);
}
return nullptr;
}
mitk::Image* mitk::SegTool2D::GetReferenceData() const
{
auto node = this->GetReferenceDataNode();
if (nullptr != node)
{
return dynamic_cast<Image*>(node->GetData());
}
return nullptr;
}
void mitk::SegTool2D::WriteBackSegmentationResult(const InteractionPositionEvent *positionEvent, const Image * segmentationResult)
{
if (!positionEvent)
return;
const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
const auto *abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry *>(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
if (planeGeometry && segmentationResult && !abstractTransformGeometry)
{
const auto workingNode = this->GetWorkingDataNode();
auto *image = dynamic_cast<Image *>(workingNode->GetData());
const auto timeStep = positionEvent->GetSender()->GetTimeStep(image);
this->WriteBackSegmentationResult(planeGeometry, segmentationResult, timeStep);
}
}
void mitk::SegTool2D::WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep)
{
if (!planeGeometry || !segmentationResult)
return;
SliceInformation sliceInfo(segmentationResult, const_cast<mitk::PlaneGeometry*>(planeGeometry), timeStep);
Self::WriteBackSegmentationResults(workingNode, { sliceInfo }, true);
}
void mitk::SegTool2D::WriteBackSegmentationResult(const PlaneGeometry *planeGeometry,
const Image * segmentationResult,
TimeStepType timeStep)
{
if (!planeGeometry || !segmentationResult)
return;
if(m_LastEventSender == nullptr)
{
return;
}
unsigned int currentSlicePosition = m_LastEventSender->GetSliceNavigationController()->GetStepper()->GetPos();
SliceInformation sliceInfo(segmentationResult, const_cast<mitk::PlaneGeometry *>(planeGeometry), timeStep);
sliceInfo.slicePosition = currentSlicePosition;
WriteBackSegmentationResults({ sliceInfo }, true);
}
void mitk::SegTool2D::WriteBackSegmentationResults(const std::vector<SegTool2D::SliceInformation> &sliceList,
bool writeSliceToVolume)
{
if (sliceList.empty())
{
return;
}
if (nullptr == m_LastEventSender)
{
- MITK_WARN << "Cannot write tool results. Tool seems to be in an invalid state, as no interaction event was recieved but is expected.";
+ MITK_WARN << "Cannot write tool results. Tool seems to be in an invalid state, as no interaction event was received but is expected.";
return;
}
const auto workingNode = this->GetWorkingDataNode();
// the first geometry is needed otherwise restoring the position is not working
const auto* plane3 =
dynamic_cast<const PlaneGeometry*>(dynamic_cast<const mitk::SlicedGeometry3D*>(
m_LastEventSender->GetSliceNavigationController()->GetCurrentGeometry3D())
->GetPlaneGeometry(0));
const unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetStepper()->GetPos();
mitk::SegTool2D::WriteBackSegmentationResults(workingNode, sliceList, writeSliceToVolume);
/* A cleaner solution would be to add a contour marker for each slice info. It currently
does not work as the contour markers expect that the plane is always the plane of slice 0.
Had not the time to do it properly no. Should be solved by T28146*/
this->AddContourmarker(plane3, slicePosition);
}
void mitk::SegTool2D::WriteBackSegmentationResults(const DataNode* workingNode, const std::vector<SliceInformation>& sliceList, bool writeSliceToVolume)
{
if (sliceList.empty())
{
return;
}
if (nullptr == workingNode)
{
mitkThrow() << "Cannot write slice to working node. Working node is invalid.";
}
auto image = dynamic_cast<Image*>(workingNode->GetData());
mitk::Label::PixelType activeLabelValue = 0;
try{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
activeLabelValue = labelSetImage->GetActiveLabel()->GetValue();
}
catch(...)
{
mitkThrow() << "Working node does not contain labelSetImage.";
}
if (nullptr == image)
{
mitkThrow() << "Cannot write slice to working node. Working node does not contain an image.";
}
for (const auto& sliceInfo : sliceList)
{
if (writeSliceToVolume && nullptr != sliceInfo.plane && sliceInfo.slice.IsNotNull())
{
SegTool2D::WriteSliceToVolume(image, sliceInfo, true);
}
}
SegTool2D::UpdateSurfaceInterpolation(sliceList, image, false, activeLabelValue);
// also mark its node as modified (T27308). Can be removed if T27307
// is properly solved
if (workingNode != nullptr) workingNode->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo)
{
SliceInformation sliceInfo(slice, planeGeometry, timeStep);
WriteSliceToVolume(workingImage, sliceInfo, allowUndo);
}
void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo)
{
if (nullptr == workingImage)
{
mitkThrow() << "Cannot write slice to working node. Working node does not contain an image.";
}
DiffSliceOperation* undoOperation = nullptr;
if (allowUndo)
{
/*============= BEGIN undo/redo feature block ========================*/
// Create undo operation by caching the not yet modified slices
mitk::Image::Pointer originalSlice = GetAffectedImageSliceAs2DImage(sliceInfo.plane, workingImage, sliceInfo.timestep);
undoOperation =
new DiffSliceOperation(workingImage,
originalSlice,
dynamic_cast<SlicedGeometry3D*>(originalSlice->GetGeometry()),
sliceInfo.timestep,
sliceInfo.plane);
/*============= END undo/redo feature block ========================*/
}
- // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk
+ // Make sure that for reslicing and overwriting the same algorithm is used. We can specify the mode of the vtk
// reslicer
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// Set the slice as 'input'
// casting const away is needed and OK as long the OverwriteMode of
// mitkVTKImageOverwrite is true.
// Reason: because then the input slice is not touched but
// used to overwrite the input of the ExtractSliceFilter.
auto noneConstSlice = const_cast<Image*>(sliceInfo.slice.GetPointer());
reslice->SetInputSlice(noneConstSlice->GetVtkImageData());
// set overwrite mode to true to write back to the image volume
reslice->SetOverwriteMode(true);
reslice->Modified();
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(workingImage);
extractor->SetTimeStep(sliceInfo.timestep);
extractor->SetWorldGeometry(sliceInfo.plane);
extractor->SetVtkOutputRequest(false);
extractor->SetResliceTransformByGeometry(workingImage->GetGeometry(sliceInfo.timestep));
extractor->Modified();
extractor->Update();
// the image was modified within the pipeline, but not marked so
workingImage->Modified();
workingImage->GetVtkImageData()->Modified();
if (allowUndo)
{
/*============= BEGIN undo/redo feature block ========================*/
// specify the redo operation with the edited slice
auto* doOperation =
new DiffSliceOperation(workingImage,
extractor->GetOutput(),
dynamic_cast<SlicedGeometry3D*>(sliceInfo.slice->GetGeometry()),
sliceInfo.timestep,
sliceInfo.plane);
// create an operation event for the undo stack
OperationEvent* undoStackItem =
new OperationEvent(DiffSliceOperationApplier::GetInstance(), doOperation, undoOperation, "Segmentation");
// add it to the undo controller
UndoStackItem::IncCurrObjectEventId();
UndoStackItem::IncCurrGroupEventId();
UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem);
/*============= END undo/redo feature block ========================*/
}
}
void mitk::SegTool2D::SetShowMarkerNodes(bool status)
{
m_ShowMarkerNodes = status;
}
void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled)
{
m_SurfaceInterpolationEnabled = enabled;
}
int mitk::SegTool2D::AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex)
{
if (planeGeometry == nullptr)
return -1;
us::ServiceReference<PlanePositionManagerService> serviceRef =
us::GetModuleContext()->GetServiceReference<PlanePositionManagerService>();
PlanePositionManagerService *service = us::GetModuleContext()->GetService(serviceRef);
unsigned int size = service->GetNumberOfPlanePositions();
unsigned int id = service->AddNewPlanePosition(planeGeometry, sliceIndex);
mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New();
mitk::Point2D p1;
planeGeometry->Map(planeGeometry->GetCenter(), p1);
contourMarker->SetPlaneGeometry(planeGeometry->Clone());
contourMarker->PlaceFigure(p1);
contourMarker->SetCurrentControlPoint(p1);
contourMarker->SetProperty("initiallyplaced", mitk::BoolProperty::New(true));
std::stringstream markerStream;
auto workingNode = this->GetWorkingDataNode();
markerStream << m_Contourmarkername;
markerStream << " ";
markerStream << id + 1;
DataNode::Pointer rotatedContourNode = DataNode::New();
rotatedContourNode->SetData(contourMarker);
rotatedContourNode->SetProperty("name", StringProperty::New(markerStream.str()));
rotatedContourNode->SetProperty("isContourMarker", BoolProperty::New(true));
rotatedContourNode->SetBoolProperty("PlanarFigureInitializedWindow", true, m_LastEventSender);
rotatedContourNode->SetProperty("includeInBoundingBox", BoolProperty::New(false));
rotatedContourNode->SetProperty("helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes));
rotatedContourNode->SetProperty("planarfigure.drawcontrolpoints", BoolProperty::New(false));
rotatedContourNode->SetProperty("planarfigure.drawname", BoolProperty::New(false));
rotatedContourNode->SetProperty("planarfigure.drawoutline", BoolProperty::New(false));
rotatedContourNode->SetProperty("planarfigure.drawshadow", BoolProperty::New(false));
if (planeGeometry)
{
if (id == size)
{
this->GetToolManager()->GetDataStorage()->Add(rotatedContourNode, workingNode);
}
else
{
mitk::NodePredicateProperty::Pointer isMarker =
mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true));
mitk::DataStorage::SetOfObjects::ConstPointer markers =
this->GetToolManager()->GetDataStorage()->GetDerivations(workingNode, isMarker);
for (auto iter = markers->begin(); iter != markers->end(); ++iter)
{
std::string nodeName = (*iter)->GetName();
unsigned int t = nodeName.find_last_of(" ");
unsigned int markerId = atof(nodeName.substr(t + 1).c_str()) - 1;
if (id == markerId)
{
return id;
}
}
this->GetToolManager()->GetDataStorage()->Add(rotatedContourNode, workingNode);
}
}
return id;
}
void mitk::SegTool2D::InteractiveSegmentationBugMessage(const std::string &message) const
{
MITK_ERROR << "********************************************************************************" << std::endl
<< " " << message << std::endl
<< "********************************************************************************" << std::endl
<< " " << std::endl
<< " If your image is rotated or the 2D views don't really contain the patient image, try to press the "
"button next to the image selection. "
<< std::endl
<< " " << std::endl
<< " Please file a BUG REPORT: " << std::endl
<< " https://phabricator.mitk.org/" << std::endl
<< " Contain the following information:" << std::endl
<< " - What image were you working on?" << std::endl
<< " - Which region of the image?" << std::endl
<< " - Which tool did you use?" << std::endl
<< " - What did you do?" << std::endl
<< " - What happened (not)? What did you expect?" << std::endl;
}
bool mitk::SegTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent* positionEvent,
const mitk::BaseData* data)
{
bool isPositionEventInsideImageRegion =
nullptr != data && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld());
if (!isPositionEventInsideImageRegion)
MITK_WARN("EditableContourTool") << "PositionEvent is outside ImageRegion!";
return isPositionEventInsideImageRegion;
}
diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h
index da50f33417..5d63e66a15 100644
--- a/Modules/Segmentation/Interactions/mitkSegTool2D.h
+++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h
@@ -1,288 +1,288 @@
/*============================================================================
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 mitkSegTool2D_h
#define mitkSegTool2D_h
#include <mitkCommon.h>
#include <mitkImage.h>
#include <mitkTool.h>
#include <MitkSegmentationExports.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkInteractionConst.h>
#include <mitkPlanePositionManager.h>
#include <mitkRestorePlanePositionOperation.h>
#include <mitkDiffSliceOperation.h>
namespace mitk
{
class BaseRenderer;
/**
\brief Abstract base class for segmentation tools.
\sa Tool
\ingroup Interaction
\ingroup ToolManagerEtAl
Implements 2D segmentation specific helper methods, that might be of use to
all kind of 2D segmentation tools. At the moment these are:
- Determination of the slice where the user paints upon (DetermineAffectedImageSlice)
- Projection of a 3D contour onto a 2D plane/slice
SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type
of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased.
Yes, your guess about when they are called is correct.
\warning Only to be instantiated by mitk::ToolManager.
$Author$
*/
class MITKSEGMENTATION_EXPORT SegTool2D : public Tool
{
public:
mitkClassMacro(SegTool2D, Tool);
/**
- \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by
+ \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index coordinates) is meant by
the plane.
\return false, if no slice direction seems right (e.g. rotated planes)
\param image
\param plane
\param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Axial --> 2
\param affectedSlice The index of the image slice
*/
static bool DetermineAffectedImageSlice(const Image *image,
const PlaneGeometry *plane,
int &affectedDimension,
int &affectedSlice);
/**
* @brief Updates the surface interpolations by extracting the contour form the given slice for all labels
* that have a surface contour information stored for the given plane at the given timestep.
* @param workingImage the segmentation image
* @param timeStep the time step for wich the surface interpolation information should be updated.
* @param plane the plane in which the slice lies
* @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the
* erosion it is most likely an intersecting contour an will not be added to the SurfaceInterpolationController
*/
static void UpdateAllSurfaceInterpolations(const LabelSetImage* workingImage,
TimeStepType timeStep,
const PlaneGeometry *plane,
bool detectIntersection);
/**
* \brief Extract the slice of an image that the user just scribbles on. The given component denotes the vector component of an vector image.
*
* \param positionEvent Event that specifies the plane that should be used to slice
* \param image Image that should be sliced
* \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component.
*
* \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem
* getting the image data at that position.
*/
static Image::Pointer GetAffectedImageSliceAs2DImage(const InteractionPositionEvent* positionEvent, const Image* image, unsigned int component = 0);
/**
* \brief Extract the slice of an image cut by given plane. The given component denotes the vector component of a vector image.
*
* \param planeGeometry Geometry defining the slice that should be cut out.
* \param image Image that should be sliced
- * \param timeStep TimeStep of the image that shold be sliced
+ * \param timeStep TimeStep of the image that should be sliced
* \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component.
*
* \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem
* getting the image data at that position.
*/
static Image::Pointer GetAffectedImageSliceAs2DImage(const PlaneGeometry* planeGeometry,
const Image* image,
TimeStepType timeStep,
unsigned int component = 0);
static Image::Pointer GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry,
const Image* image,
TimePointType timePoint,
unsigned int component = 0);
/** Convenience overloaded version that can be called for a given planeGeometry, slice image and time step.
* Calls static WriteBackSegmentationResults*/
static void WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep);
/** Convenience overloaded version that can be called for a given planeGeometry, slice image and time step.
* For more details see protected WriteSliceToVolume version.*/
static void WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo);
void SetShowMarkerNodes(bool);
/**
* \brief Enables or disables the 3D interpolation after writing back the 2D segmentation result, and defaults to
* true.
*/
void SetEnable3DInterpolation(bool);
void Activated() override;
void Deactivated() override;
itkSetMacro(IsTimePointChangeAware, bool);
itkGetMacro(IsTimePointChangeAware, bool);
itkBooleanMacro(IsTimePointChangeAware);
protected:
SegTool2D(); // purposely hidden
SegTool2D(const char *, const us::Module *interactorModule = nullptr); // purposely hidden
~SegTool2D() override;
/**
* @brief returns the segmentation node that should be modified by the tool.
*/
DataNode* GetWorkingDataNode() const;
Image* GetWorkingData() const;
DataNode* GetReferenceDataNode() const;
Image* GetReferenceData() const;
/**
* This function can be reimplemented by derived classes to react on changes of the current
* time point. Default implementation does nothing.*/
virtual void OnTimePointChanged();
struct SliceInformation
{
mitk::Image::ConstPointer slice;
const mitk::PlaneGeometry *plane = nullptr;
mitk::TimeStepType timestep = 0;
unsigned int slicePosition;
SliceInformation() = default;
SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep);
};
/**
* @brief Updates the surface interpolation by extracting the contour form the given slice.
* @param sliceInfos vector of slice information instances from which the contours should be extracted
* @param workingImage the segmentation image
* @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the
* @param activeLabelValue The label value of the active label.
* @param silent Indicates if the modification event of the SurfaceInterpolationController should be triggered.
* erosion it is most
* likely an intersecting contour an will not be added to the SurfaceInterpolationController
*/
static void UpdateSurfaceInterpolation(const std::vector<SliceInformation>& sliceInfos,
const Image* workingImage,
bool detectIntersection,
mitk::Label::PixelType activeLabelValue, bool silent = false);
/**
* \brief Filters events that cannot be handled by 2D segmentation tools
*
* Currently an event is discarded if it was not sent by a 2D renderwindow and if it is
* not of type InteractionPositionEvent
*/
bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override;
/**
\brief Extract the slice of the currently selected working image that the user just scribbles on.
\return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem
getting the image data at that position,
or just no working image is selected.
*/
Image::Pointer GetAffectedWorkingSlice(const InteractionPositionEvent *) const;
/**
\brief Extract the slice of the currently selected reference image that the user just scribbles on.
\return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem
getting the image data at that position,
or just no reference image is selected.
*/
Image::Pointer GetAffectedReferenceSlice(const InteractionPositionEvent *) const;
/** Overload version that gets the reference slice passed on the passed plane geometry and timestep.*/
Image::Pointer GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const;
/** Convenience version that can be called for a given event (which is used to deduce timepoint and plane) and a slice image.
* Calls non static WriteBackSegmentationResults*/
void WriteBackSegmentationResult(const InteractionPositionEvent *, const Image* segmentationResult);
/** Convenience version that can be called for a given planeGeometry, slice image and time step.
* Calls non static WriteBackSegmentationResults*/
void WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, const Image* segmentationResult, TimeStepType timeStep);
/** Overloaded version that calls the static version and also adds the contour markers.
* @remark If the sliceList is empty, this function does nothing.*/
void WriteBackSegmentationResults(const std::vector<SliceInformation> &sliceList, bool writeSliceToVolume = true);
/** \brief Writes all provided source slices into the data of the passed workingNode.
* The function does the following: 1) for every passed slice write it to workingNode (and generate and undo/redo step);
* 2) update the surface interpolation and 3) mark the node as modified.
* @param workingNode Pointer to the node that contains the working image.
* @param sliceList Vector of all slices that should be written into the workingNode. If the list is
* empty, the function call does nothing.
* @param writeSliceToVolume If set to false the write operation (WriteSliceToVolume will be skipped)
* and only the surface interpolation will be updated.
* @pre workingNode must point to a valid instance and contain an image instance as data.*/
static void WriteBackSegmentationResults(const DataNode* workingNode, const std::vector<SliceInformation>& sliceList, bool writeSliceToVolume = true);
/** Writes a provided slice into the passed working image. The content of working image that is covered
- * by the slice will be completly overwritten. If asked for it also generates the needed
+ * by the slice will be completely overwritten. If asked for it also generates the needed
* undo/redo steps.
* @param workingImage Pointer to the image that is the target of the write operation.
- * @param sliceInfo SliceInfo instance that containes the slice image, the defining plane geometry and time step.
+ * @param sliceInfo SliceInfo instance that contains the slice image, the defining plane geometry and time step.
* @param allowUndo Indicates if undo/redo operations should be registered for the write operation
* performed by this call. true: undo/redo will be generated; false: no undo/redo will be generated, so
* this operation cannot be revoked by the user.
* @pre workingImage must point to a valid instance.*/
static void WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo);
/**
\brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure.
By selecting this node the slicestack will be reoriented according to the passed
PlanarFigure's Geometry
*/
int AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex);
void InteractiveSegmentationBugMessage(const std::string &message) const;
/** Helper function to check if a position events points to a point inside the boundingbox of a passed
data instance.*/
static bool IsPositionEventInsideImageRegion(InteractionPositionEvent* positionEvent, const BaseData* data);
BaseRenderer *m_LastEventSender = nullptr;
unsigned int m_LastEventSlice = 0;
itkGetMacro(LastTimePointTriggered, TimePointType);
private:
/** Internal method that gets triggered as soon as the tool manager indicates a
* time point change. If the time point has changed since last time and tool
* is set to be time point change aware, OnTimePointChanged() will be called.*/
void OnTimePointChangedInternal();
static void RemoveContourFromInterpolator(const SliceInformation& sliceInfo, LabelSetImage::LabelValueType labelValue);
// The prefix of the contourmarkername. Suffix is a consecutive number
const std::string m_Contourmarkername;
bool m_ShowMarkerNodes = false;
static bool m_SurfaceInterpolationEnabled;
bool m_IsTimePointChangeAware = true;
TimePointType m_LastTimePointTriggered = 0.;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h
index 4aa461a30e..ac7d7a7bb1 100644
--- a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h
+++ b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h
@@ -1,339 +1,339 @@
/*============================================================================
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 mitkSegWithPreviewTool_h
#define mitkSegWithPreviewTool_h
#include "mitkTool.h"
#include "mitkCommon.h"
#include "mitkDataNode.h"
#include "mitkToolCommand.h"
#include <MitkSegmentationExports.h>
namespace mitk
{
/**
\brief Base class for any auto segmentation tool that provides a preview of the new segmentation.
This tool class implements a lot basic logic to handle auto segmentation tools with preview,
Time point and ROI support. Derived classes will ask to update the segmentation preview if needed
(e.g. because the ROI or the current time point has changed) or because derived tools
indicated the need to update themselves.
- This class also takes care to properly transfer a confirmed preview into the segementation
+ This class also takes care to properly transfer a confirmed preview into the segmentation
result.
\ingroup ToolManagerEtAl
\sa mitk::Tool
\sa QmitkInteractiveSegmentation
*/
class MITKSEGMENTATION_EXPORT SegWithPreviewTool : public Tool
{
public:
mitkClassMacro(SegWithPreviewTool, Tool);
void Activated() override;
void Deactivated() override;
void ConfirmSegmentation();
itkSetMacro(CreateAllTimeSteps, bool);
itkGetMacro(CreateAllTimeSteps, bool);
itkBooleanMacro(CreateAllTimeSteps);
itkSetMacro(KeepActiveAfterAccept, bool);
itkGetMacro(KeepActiveAfterAccept, bool);
itkBooleanMacro(KeepActiveAfterAccept);
itkSetMacro(IsTimePointChangeAware, bool);
itkGetMacro(IsTimePointChangeAware, bool);
itkBooleanMacro(IsTimePointChangeAware);
itkSetMacro(ResetsToEmptyPreview, bool);
itkGetMacro(ResetsToEmptyPreview, bool);
itkBooleanMacro(ResetsToEmptyPreview);
itkSetMacro(UseSpecialPreviewColor, bool);
itkGetMacro(UseSpecialPreviewColor, bool);
itkBooleanMacro(UseSpecialPreviewColor);
itkSetMacro(RequestDeactivationConfirmation, bool);
itkGetMacro(RequestDeactivationConfirmation, bool);
itkBooleanMacro(RequestDeactivationConfirmation);
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle);
itkGetMacro(MergeStyle, MultiLabelSegmentation::MergeStyle);
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle);
itkGetMacro(OverwriteStyle, MultiLabelSegmentation::OverwriteStyle);
enum class LabelTransferScope
{
- ActiveLabel, //Only the selected label will be transfered from the preview segmentation
+ ActiveLabel, //Only the selected label will be transferred from the preview segmentation
//to the result segmentation.
//If this mode is selected the class expects that GetSelectedLabels indicate
//the label in the preview.
- SelectedLabels, //The labels defined as selected labels will be transfered.
+ SelectedLabels, //The labels defined as selected labels will be transferred.
AllLabels //Transfer all labels of the preview
};
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetLabelTransferScope(LabelTransferScope labelTransferScope);
itkGetMacro(LabelTransferScope, LabelTransferScope);
using SelectedLabelVectorType = std::vector<Label::PixelType>;
- /** Specifies the labels that should be transfered form preview to the working image,
+ /** Specifies the labels that should be transferred form preview to the working image,
if the segmentation is confirmed. The setting will be used, if LabelTransferScope is set to "ActiveLabel"
or "SelectedLabels".
@remark If the LabelTransferScope=="ActiveLabel", the class expects only one label to be selected.
- @remark The selected label IDs corespond to the labels of the preview image.*/
+ @remark The selected label IDs correspond to the labels of the preview image.*/
void SetSelectedLabels(const SelectedLabelVectorType& labelsToTransfer);
itkGetMacro(SelectedLabels, SelectedLabelVectorType);
enum class LabelTransferMode
{
- MapLabel, //Only the active label will be transfered from preview to segmentation.
- AddLabel //The labels defined as selected labels will be transfered.
+ MapLabel, //Only the active label will be transferred from preview to segmentation.
+ AddLabel //The labels defined as selected labels will be transferred.
};
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetLabelTransferMode(LabelTransferMode labelTransferMode);
itkGetMacro(LabelTransferMode, LabelTransferMode);
bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override;
/** Triggers the actualization of the preview
* @param ignoreLazyPreviewSetting If set true UpdatePreview will always
* generate the preview for all time steps. If set to false, UpdatePreview
* will regard the setting specified by the constructor.
* To define the update generation for time steps implement DoUpdatePreview.
* To alter what should be done directly before or after the update of the preview,
* reimplement UpdatePrepare() or UpdateCleanUp().*/
void UpdatePreview(bool ignoreLazyPreviewSetting = false);
/** Indicate if currently UpdatePreview is triggered (true) or not (false).*/
bool IsUpdating() const;
/**
* @brief Gets the name of the currently selected segmentation node
* @return the name of the segmentation node or an empty string if
* none is selected
*/
std::string GetCurrentSegmentationName();
/**
* @brief Returns the currently selected segmentation node
* @return a mitk::DataNode which contains a segmentation image
*/
virtual DataNode* GetTargetSegmentationNode() const;
LabelSetImage* GetTargetSegmentation() const;
/** Returns the image that contains the preview of the current segmentation.
* Returns null if the node is not set or does not contain an image.*/
LabelSetImage* GetPreviewSegmentation();
const LabelSetImage* GetPreviewSegmentation() const;
DataNode* GetPreviewSegmentationNode();
protected:
ToolCommand::Pointer m_ProgressCommand;
SegWithPreviewTool(bool lazyDynamicPreviews = false); // purposely hidden
SegWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden
~SegWithPreviewTool() override;
const char* GetGroup() const override;
/** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/
static Image::ConstPointer GetImageByTimeStep(const Image* image, TimeStepType timestep);
/** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/
static Image::Pointer GetImageByTimeStep(Image* image, TimeStepType timestep);
/** Helper that extracts the image for the passed time point, if the image has multiple time steps.*/
static Image::ConstPointer GetImageByTimePoint(const Image* image, TimePointType timePoint);
void EnsureTargetSegmentationNodeInDataStorage() const;
/** Member is always called if GetSegmentationInput() has changed
* (e.g. because a new ROI was defined, or on activation) to give derived
- * classes the posibility to initiate their state accordingly.
+ * classes the possibility to initiate their state accordingly.
* Reimplement this function to implement special behavior.
*/
virtual void InitiateToolByInput();
/** This member function offers derived classes the possibility to alter what should
happen directly before the update of the preview is performed. It is called by
UpdatePreview. Default implementation does nothing.*/
virtual void UpdatePrepare();
/** This member function offers derived classes the possibility to alter what should
happen directly after the update of the preview is performed. It is called by
UpdatePreview. Default implementation does nothing.*/
virtual void UpdateCleanUp();
/** This member function offers derived classes the possibility to alter what should
happen directly after the Confirmation of the preview is performed. It is called by
ConfirmSegmentation. Default implementation does nothing.*/
virtual void ConfirmCleanUp();
using LabelMappingType = std::vector<std::pair<Label::PixelType, Label::PixelType> >;
/** This member function offers derived classes the possibility to alter what should
- happen directly before the content of the preview is transfered to the segmentation,
+ happen directly before the content of the preview is transferred to the segmentation,
when the segmentation is confirmed. It is called by CreateResultSegmentationFromPreview.
- Default implementation ensure that all labels that will be transfered, exist in the
+ Default implementation ensure that all labels that will be transferred, exist in the
segmentation. If they are not existing before the transfer, the will be added by
cloning the label information of the preview.
- @param labelMapping the mapping that should be used for transfering the labels.
+ @param labelMapping the mapping that should be used for transferring the labels.
*/
virtual void PreparePreviewToResultTransfer(const LabelMappingType& labelMapping);
static void TransferLabelInformation(const LabelMappingType& labelMapping,
const mitk::LabelSetImage* source, mitk::LabelSetImage* target);
/**Helper function that can be used to move the content of an LabelSetImage (the pixels of the active source layer and the labels).
- This is e.g. helpfull if you generate an LabelSetImage content in DoUpdatePreview and you want to transfer it into the preview image.*/
+ This is e.g. helpful if you generate an LabelSetImage content in DoUpdatePreview and you want to transfer it into the preview image.*/
static void TransferLabelSetImageContent(const LabelSetImage* source, LabelSetImage* target, TimeStepType timeStep);
/** This function does the real work. Here the preview for a given
* input image should be computed and stored in the also passed
* preview image at the passed time step.
* It also provides the current/old segmentation at the time point,
- * which can be used, if the preview depends on the the segmenation so far.
+ * which can be used, if the preview depends on the the segmentation so far.
*/
virtual void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) = 0;
/** Returns the input that should be used for any segmentation/preview or tool update.
* It is either the data of ReferenceDataNode itself or a part of it defined by a ROI mask
* provided by the tool manager. Derived classes should regard this as the relevant
* input data for any processing.
* Returns null if the node is not set or does not contain an image.*/
const Image* GetSegmentationInput() const;
/** Returns the image that is provided by the ReferenceDataNode.
* Returns null if the node is not set or does not contain an image.*/
const Image* GetReferenceData() const;
/** Resets the preview node so it is empty and ready to be filled by the tool
@remark Calling this function will generate a new preview image, and the old
might be invalidated. Therefore this function should not be used within the
scope of UpdatePreview (m_IsUpdating == true).*/
void ResetPreviewNode();
/** Resets the complete content of the preview image. The instance of the preview image and its settings
* stay the same.*/
void ResetPreviewContent();
/** Resets only the image content of the specified timeStep of the preview image. If the preview image or the specified
time step does not exist, nothing happens.*/
void ResetPreviewContentAtTimeStep(unsigned int timeStep);
TimePointType GetLastTimePointOfUpdate() const;
LabelSetImage::LabelValueType GetActiveLabelValueOfPreview() const;
itkGetConstMacro(UserDefinedActiveLabel, Label::PixelType);
itkSetObjectMacro(WorkingPlaneGeometry, PlaneGeometry);
itkGetConstObjectMacro(WorkingPlaneGeometry, PlaneGeometry);
bool ConfirmBeforeDeactivation() override;
private:
void TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep, const LabelMappingType& labelMapping);
void CreateResultSegmentationFromPreview();
void OnRoiDataChanged();
void OnTimePointChanged();
/**Internal helper that ensures that the stored active label is up to date.
This is a fix for T28131 / T28986. It should be refactored if T28524 is being worked on.
On the long run, the active label will be communicated/set by the user/toolmanager as a
state of the tool and the tool should react accordingly (like it does for other external
state changes).
@return indicates if the label has changed (true) or not.
*/
bool EnsureUpToDateUserDefinedActiveLabel();
/**Returns that label mapping between preview segmentation (first element of pair) and
result segmentation (second element of pair).
The content depends on the settings of LabelTransferMode and LabelTransferScope*/
LabelMappingType GetLabelMapping() const;
- /** Node that containes the preview data generated and managed by this class or derived ones.*/
+ /** Node that contains the preview data generated and managed by this class or derived ones.*/
DataNode::Pointer m_PreviewSegmentationNode;
- /** The reference data recieved from ToolManager::GetReferenceData when tool was activated.*/
+ /** The reference data received from ToolManager::GetReferenceData when tool was activated.*/
DataNode::Pointer m_ReferenceDataNode;
- /** Node that containes the data that should be used as input for any auto segmentation. It might
+ /** Node that contains the data that should be used as input for any auto segmentation. It might
* be the same like m_ReferenceDataNode (if no ROI is set) or a sub region (if ROI is set).*/
DataNode::Pointer m_SegmentationInputNode;
/** Indicates if Accepting the threshold should transfer/create the segmentations
of all time steps (true) or only of the currently selected timepoint (false).*/
bool m_CreateAllTimeSteps = false;
/** Indicates if the tool should kept active after accepting the segmentation or not.*/
bool m_KeepActiveAfterAccept = false;
/** Relevant if the working data / preview image has multiple time steps (dynamic segmentations).
* This flag has to be set by derived classes accordingly to there way to generate dynamic previews.
* If LazyDynamicPreview is true, the tool generates only the preview for the current time step.
* Therefore it always has to update the preview if current time point has changed and it has to (re)compute
* all timeframes if ConfirmSegmentation() is called.*/
bool m_LazyDynamicPreviews = false;
bool m_IsTimePointChangeAware = true;
/** Controls if ResetPreviewNode generates an empty content (true) or clones the current
segmentation (false).*/
bool m_ResetsToEmptyPreview = false;
/** Controls if for the preview of the active label a special preview color is used.
* If set to false, coloring will stay in the preview like it is in the working image.*/
bool m_UseSpecialPreviewColor = true;
TimePointType m_LastTimePointOfUpdate = 0.;
bool m_IsUpdating = false;
Label::PixelType m_UserDefinedActiveLabel = 1;
/** This variable indicates if for the tool a working plane geometry is defined.
* If a working plane is defined the tool will only work an the slice of the input
* and the segmentation. Thus only the relevant input slice will be passed to
- * DoUpdatePreview(...) and only the relevant slice of the preview will be transfered when
+ * DoUpdatePreview(...) and only the relevant slice of the preview will be transferred when
* ConfirmSegmentation() is called.*/
PlaneGeometry::Pointer m_WorkingPlaneGeometry;
- /** This variable controles how the label pixel content of the preview should be transfered into the
+ /** This variable controls how the label pixel content of the preview should be transferred into the
segmentation- For more details of the behavior see documentation of MultiLabelSegmentation::MergeStyle. */
MultiLabelSegmentation::MergeStyle m_MergeStyle = MultiLabelSegmentation::MergeStyle::Replace;
- /** This variable controles how the label pixel content of the preview should be transfered into the
+ /** This variable controls how the label pixel content of the preview should be transferred into the
segmentation- For more details of the behavior see documentation of MultiLabelSegmentation::OverwriteStyle. */
MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = MultiLabelSegmentation::OverwriteStyle::RegardLocks;
LabelTransferScope m_LabelTransferScope = LabelTransferScope::ActiveLabel;
SelectedLabelVectorType m_SelectedLabels = {};
LabelTransferMode m_LabelTransferMode = LabelTransferMode::MapLabel;
bool m_IsPreviewGenerated = false;
/** This variable tracks if there should be a user-confirmation before a tool is deactivated or not.
* Call RequestDeactivationConfirmationOn() in the tool class to avail this feature.
*/
bool m_RequestDeactivationConfirmation = false;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h
index 6d75fe8d34..ca4b68fe21 100644
--- a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h
+++ b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h
@@ -1,150 +1,150 @@
/*============================================================================
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 MITKTOTALSEGMENTATORTOOL_H
#define MITKTOTALSEGMENTATORTOOL_H
#include "mitkSegWithPreviewTool.h"
#include <MitkSegmentationExports.h>
#include "mitkProcessExecutor.h"
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
\brief TotalSegmentator segmentation tool.
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT TotalSegmentatorTool : public SegWithPreviewTool
{
public:
mitkClassMacro(TotalSegmentatorTool, SegWithPreviewTool);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const char *GetName() const override;
const char **GetXPM() const override;
us::ModuleResource GetIconResource() const override;
void Activated() override;
itkSetMacro(MitkTempDir, std::string);
itkGetConstMacro(MitkTempDir, std::string);
itkSetMacro(SubTask, std::string);
itkGetConstMacro(SubTask, std::string);
itkSetMacro(PythonPath, std::string);
itkGetConstMacro(PythonPath, std::string);
itkSetMacro(GpuId, unsigned int);
itkGetConstMacro(GpuId, unsigned int);
itkSetMacro(Fast, bool);
itkGetConstMacro(Fast, bool);
itkBooleanMacro(Fast);
/**
* @brief Static function to print out everything from itk::EventObject.
* Used as callback in mitk::ProcessExecutor object.
*
*/
static void onPythonProcessEvent(itk::Object *, const itk::EventObject &e, void *);
protected:
TotalSegmentatorTool();
~TotalSegmentatorTool();
/**
- * @brief Overriden method from the tool manager to execute the segmentation
+ * @brief Overridden method from the tool manager to execute the segmentation
* Implementation:
* 1. Creates temp directory, if not done already.
* 2. Parses Label names from map_to_binary.py for using later on.
* 3. Calls "run_totalsegmentator" method.
- * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as
+ * 4. Expects an output image to be saved in the temporary directory by the python process. Loads it as
* LabelSetImage and sets to previewImage.
*
* @param inputAtTimeStep
* @param oldSegAtTimeStep
* @param previewImage
* @param timeStep
*/
void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override;
void UpdatePrepare() override;
private:
/**
* @brief Runs Totalsegmentator python process with desired arguments
*
*/
void run_totalsegmentator(ProcessExecutor*, const std::string&, const std::string&, bool, bool, unsigned int, const std::string&);
/**
* @brief Applies the m_LabelMapTotal lookup table on the output segmentation LabelSetImage.
*
*/
void MapLabelsToSegmentation(const mitk::LabelSetImage*, mitk::LabelSetImage*, std::map<mitk::Label::PixelType, std::string>&);
/**
* @brief Parses map_to_binary.py file to extract label ids and names
* and stores as a map for reference in m_LabelMapTotal
*
*/
void ParseLabelMapTotalDefault();
/**
* @brief Get the Label Map Path from the virtual environment location
*
* @return std::string
*/
std::string GetLabelMapPath();
/**
* @brief Agglomerate many individual mask image files into one multi-label LabelSetImage in the
* given filePath order.
*
* @param filePaths
* @param dimension
* @param geometry
* @return LabelSetImage::Pointer
*/
LabelSetImage::Pointer AgglomerateLabelFiles(std::vector<std::string>& filePaths, const unsigned int* dimension, mitk::BaseGeometry* geometry);
std::string m_MitkTempDir;
std::string m_PythonPath;
std::string m_SubTask = "total";
unsigned int m_GpuId = 0;
std::map<mitk::Label::PixelType, std::string> m_LabelMapTotal;
bool m_Fast = true;
const std::string TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz";
const std::string DEFAULT_TOTAL_TASK = "total";
const std::unordered_map<std::string, std::vector<std::string>> SUBTASKS_MAP =
{
{"body", { "body.nii.gz", "body_trunc.nii.gz", "body_extremities.nii.gz", "skin.nii.gz"}},
{"hip_implant", {"hip_implant.nii.gz"}},
{"cerebral_bleed", {"intracerebral_hemorrhage.nii.gz"}},
{"coronary_arteries", {"coronary_arteries.nii.gz"}},
{"lung_vessels", {"lung_vessels.nii.gz", "lung_trachea_bronchia.nii.gz"}},
{"pleural_pericard_effusion", {"pleural_effusion.nii.gz", "pericardial_effusion.nii.gz"}}
};
}; // class
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.h b/Modules/Segmentation/Interactions/mitknnUnetTool.h
index e6e3fc9bb1..8172cca4b7 100644
--- a/Modules/Segmentation/Interactions/mitknnUnetTool.h
+++ b/Modules/Segmentation/Interactions/mitknnUnetTool.h
@@ -1,215 +1,215 @@
/*============================================================================
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 mitknnUnetTool_h
#define mitknnUnetTool_h
#include "mitkSegWithPreviewTool.h"
#include "mitkCommon.h"
#include "mitkToolManager.h"
#include <MitkSegmentationExports.h>
#include <mitkStandardFileLocations.h>
#include <numeric>
#include <utility>
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
* @brief nnUNet parameter request object holding all model parameters for input.
* Also holds output temporary directory path.
*/
struct ModelParams
{
std::string task;
std::vector<std::string> folds;
std::string model;
std::string trainer;
std::string planId;
std::string outputDir;
std::string inputName;
std::string timeStamp;
size_t generateHash() const
{
std::string toHash;
std::string foldsConcatenated = std::accumulate(folds.begin(), folds.end(), std::string(""));
toHash += this->task;
toHash += this->model;
toHash += this->inputName;
toHash += foldsConcatenated;
toHash += this->timeStamp;
size_t hashVal = std::hash<std::string>{}(toHash);
return hashVal;
}
};
/**
\brief nnUNet segmentation tool.
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT nnUNetTool : public SegWithPreviewTool
{
public:
mitkClassMacro(nnUNetTool, SegWithPreviewTool);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const char **GetXPM() const override;
const char *GetName() const override;
us::ModuleResource GetIconResource() const override;
void Activated() override;
itkSetMacro(nnUNetDirectory, std::string);
itkGetConstMacro(nnUNetDirectory, std::string);
itkSetMacro(ModelDirectory, std::string);
itkGetConstMacro(ModelDirectory, std::string);
itkSetMacro(PythonPath, std::string);
itkGetConstMacro(PythonPath, std::string);
itkSetMacro(MitkTempDir, std::string);
itkGetConstMacro(MitkTempDir, std::string);
itkSetMacro(PostProcessingJsonDirectory, std::string);
itkGetConstMacro(PostProcessingJsonDirectory, std::string);
itkSetMacro(MixedPrecision, bool);
itkGetConstMacro(MixedPrecision, bool);
itkBooleanMacro(MixedPrecision);
itkSetMacro(Mirror, bool);
itkGetConstMacro(Mirror, bool);
itkBooleanMacro(Mirror);
itkSetMacro(MultiModal, bool);
itkGetConstMacro(MultiModal, bool);
itkBooleanMacro(MultiModal);
itkSetMacro(NoPip, bool);
itkGetConstMacro(NoPip, bool);
itkBooleanMacro(NoPip);
itkSetMacro(Ensemble, bool);
itkGetConstMacro(Ensemble, bool);
itkBooleanMacro(Ensemble);
itkSetMacro(Predict, bool);
itkGetConstMacro(Predict, bool);
itkBooleanMacro(Predict);
itkSetMacro(GpuId, unsigned int);
itkGetConstMacro(GpuId, unsigned int);
/**
* @brief vector of ModelParams.
* Size > 1 only for ensemble prediction.
*/
std::vector<ModelParams> m_ParamQ;
/**
* @brief Holds paths to other input image modalities.
*
*/
std::vector<mitk::Image::ConstPointer> m_OtherModalPaths;
mitk::Image::ConstPointer m_InputBuffer;
/**
* @brief Renders the output LabelSetImage.
* To called in the main thread.
*/
void RenderOutputBuffer();
/**
* @brief Get the Output Buffer object
*
* @return LabelSetImage::Pointer
*/
LabelSetImage::Pointer GetOutputBuffer();
/**
* @brief Sets the outputBuffer to nullptr
*
*/
void ClearOutputBuffer();
/**
* @brief Returns the DataStorage from the ToolManager
*/
mitk::DataStorage *GetDataStorage();
mitk::DataNode *GetRefNode();
void SetOutputBuffer(LabelSetImage::Pointer);
protected:
/**
* @brief Construct a new nnUNet Tool object.
*
*/
nnUNetTool() = default;
/**
* @brief Destroy the nnUNet Tool object and deletes the temp directory.
*
*/
~nnUNetTool();
/**
- * @brief Overriden method from the tool manager to execute the segmentation
+ * @brief Overridden method from the tool manager to execute the segmentation
* Implementation:
* 1. Saves the inputAtTimeStep in a temporary directory.
* 2. Copies other modalities, renames and saves in the temporary directory, if required.
* 3. Sets RESULTS_FOLDER and CUDA_VISIBLE_DEVICES variables in the environment.
* 3. Iterates through the parameter queue (m_ParamQ) and executes "nnUNet_predict" command with the parameters
- * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as
+ * 4. Expects an output image to be saved in the temporary directory by the python process. Loads it as
* LabelSetImage and sets to previewImage.
*
* @param inputAtTimeStep
* @param oldSegAtTimeStep
* @param previewImage
* @param timeStep
*/
void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override;
void UpdatePrepare() override;
private:
std::string m_MitkTempDir;
std::string m_nnUNetDirectory;
std::string m_ModelDirectory;
std::string m_PythonPath;
std::string m_PostProcessingJsonDirectory;
// bool m_UseGPU; kept for future
// bool m_AllInGPU;
bool m_MixedPrecision;
bool m_Mirror;
bool m_NoPip;
bool m_MultiModal;
bool m_Ensemble = false;
bool m_Predict;
LabelSetImage::Pointer m_OutputBuffer;
unsigned int m_GpuId;
const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz";
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/Rendering/mitkContourMapper2D.h b/Modules/Segmentation/Rendering/mitkContourMapper2D.h
index 568a7b4b12..cedd39905a 100644
--- a/Modules/Segmentation/Rendering/mitkContourMapper2D.h
+++ b/Modules/Segmentation/Rendering/mitkContourMapper2D.h
@@ -1,60 +1,60 @@
/*============================================================================
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 mitkContourMapper2D_h
#define mitkContourMapper2D_h
#include "mitkCommon.h"
#include "mitkMapper.h"
#include <MitkSegmentationExports.h>
namespace mitk
{
class BaseRenderer;
class Contour;
/**
* @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window
*
*
* @ingroup Mapper
*/
class MITKSEGMENTATION_EXPORT ContourMapper2D : public Mapper
{
public:
mitkClassMacro(ContourMapper2D, Mapper);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* reimplemented from Baseclass
*/
void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override;
void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * actor=nullptr) override;
/**
- * return a refernce of the rendered data object
+ * return a reference of the rendered data object
*/
const Contour *GetInput(void);
protected:
ContourMapper2D();
~ContourMapper2D() override;
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h
index a913f2137b..d4692fcfa5 100644
--- a/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h
+++ b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h
@@ -1,60 +1,60 @@
/*============================================================================
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 mitkContourSetMapper2D_h
#define mitkContourSetMapper2D_h
#include "mitkCommon.h"
#include "mitkGLMapper.h"
#include <MitkSegmentationExports.h>
namespace mitk
{
class BaseRenderer;
class ContourSet;
/**
* @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window
*
*
* @ingroup Mapper
*/
class MITKSEGMENTATION_EXPORT ContourSetMapper2D : public Mapper
{
public:
mitkClassMacro(ContourSetMapper2D, Mapper);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* reimplemented from Baseclass
*/
void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override;
void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * actor = nullptr) override;
/**
- * return a refernce of the rendered data object
+ * return a reference of the rendered data object
*/
const mitk::ContourSet *GetInput(void);
protected:
ContourSetMapper2D();
~ContourSetMapper2D() override;
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp
index e5839fa022..3e9277df26 100644
--- a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp
+++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp
@@ -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.
============================================================================*/
#include <mitkTestingMacros.h>
#include <mitkExtractSliceFilter.h>
#include <mitkGeometry3D.h>
#include <mitkImageCast.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkInteractionConst.h>
#include <mitkRotationOperation.h>
#include <mitkVtkImageOverwrite.h>
#include <itkImage.h>
#include <itkImageRegionIterator.h>
#include <vtkImageData.h>
#include <vtkSmartPointer.h>
int ObliquePlaneTestVolumeSize = 128;
static void OverwriteObliquePlaneTest(mitk::Image *workingImage, mitk::Image *refImg)
{
- /*==============TEST WITHOUT MITK CONVERTION=============================*/
+ /*==============TEST WITHOUT MITK CONVERSION=============================*/
/* ============= setup plane ============*/
auto sliceindex = (int)(ObliquePlaneTestVolumeSize / 2); // rand() % 32;
bool isFrontside = true;
bool isRotated = false;
mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New();
obliquePlane->InitializeStandardPlane(
workingImage->GetGeometry(), mitk::AnatomicalPlane::Axial, sliceindex, isFrontside, isRotated);
mitk::Point3D origin = obliquePlane->GetOrigin();
mitk::Vector3D normal;
normal = obliquePlane->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
obliquePlane->SetOrigin(origin);
mitk::Vector3D rotationVector = obliquePlane->GetAxisVector(0);
rotationVector.Normalize();
float degree = 45.0;
auto op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree);
obliquePlane->ExecuteOperation(op);
delete op;
/* ============= extract slice ============*/
mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New();
slicer->SetInput(workingImage);
slicer->SetWorldGeometry(obliquePlane);
slicer->SetVtkOutputRequest(true);
slicer->Modified();
slicer->Update();
vtkSmartPointer<vtkImageData> slice = vtkSmartPointer<vtkImageData>::New();
slice = slicer->GetVtkOutput();
/* ============= overwrite slice ============*/
vtkSmartPointer<mitkVtkImageOverwrite> resliceIdx = vtkSmartPointer<mitkVtkImageOverwrite>::New();
mitk::ExtractSliceFilter::Pointer overwriter = mitk::ExtractSliceFilter::New(resliceIdx);
resliceIdx->SetOverwriteMode(true);
resliceIdx->SetInputSlice(slice);
resliceIdx->Modified();
overwriter->SetInput(workingImage);
overwriter->SetWorldGeometry(obliquePlane);
overwriter->SetVtkOutputRequest(true);
overwriter->Modified();
overwriter->Update();
typedef mitk::ImagePixelReadAccessor<unsigned short, 3> ReadAccessorType;
ReadAccessorType refImgReadAccessor(refImg);
ReadAccessorType workingImgReadAccessor(workingImage);
/* ============= check ref == working ============*/
bool areSame = true;
itk::Index<3> id;
id[0] = id[1] = id[2] = 0;
for (int x = 0; x < ObliquePlaneTestVolumeSize; ++x)
{
id[0] = x;
for (int y = 0; y < ObliquePlaneTestVolumeSize; ++y)
{
id[1] = y;
for (int z = 0; z < ObliquePlaneTestVolumeSize; ++z)
{
id[2] = z;
areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id);
if (!areSame)
goto stop;
}
}
}
stop:
- MITK_TEST_CONDITION(areSame, "comparing images (no mitk convertion) [oblique]");
+ MITK_TEST_CONDITION(areSame, "comparing images (no mitk conversion) [oblique]");
- /*==============TEST WITH MITK CONVERTION=============================*/
+ /*==============TEST WITH MITK CONVERSION=============================*/
/* ============= extract slice ============*/
mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New();
slicer2->SetInput(workingImage);
slicer2->SetWorldGeometry(obliquePlane);
slicer2->Modified();
slicer2->Update();
mitk::Image::Pointer sliceInMitk = slicer2->GetOutput();
vtkSmartPointer<vtkImageData> slice2 = vtkSmartPointer<vtkImageData>::New();
slice2 = sliceInMitk->GetVtkImageData();
/* ============= overwrite slice ============*/
vtkSmartPointer<mitkVtkImageOverwrite> resliceIdx2 = vtkSmartPointer<mitkVtkImageOverwrite>::New();
mitk::ExtractSliceFilter::Pointer overwriter2 = mitk::ExtractSliceFilter::New(resliceIdx2);
resliceIdx2->SetOverwriteMode(true);
resliceIdx2->SetInputSlice(slice2);
resliceIdx2->Modified();
overwriter2->SetInput(workingImage);
overwriter2->SetWorldGeometry(obliquePlane);
overwriter2->SetVtkOutputRequest(true);
overwriter2->Modified();
overwriter2->Update();
/* ============= check ref == working ============*/
areSame = true;
id[0] = id[1] = id[2] = 0;
for (int x = 0; x < ObliquePlaneTestVolumeSize; ++x)
{
id[0] = x;
for (int y = 0; y < ObliquePlaneTestVolumeSize; ++y)
{
id[1] = y;
for (int z = 0; z < ObliquePlaneTestVolumeSize; ++z)
{
id[2] = z;
areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id);
if (!areSame)
goto stop2;
}
}
}
stop2:
- MITK_TEST_CONDITION(areSame, "comparing images (with mitk convertion) [oblique]");
+ MITK_TEST_CONDITION(areSame, "comparing images (with mitk conversion) [oblique]");
- /*==============TEST EDIT WITHOUT MITK CONVERTION=============================*/
+ /*==============TEST EDIT WITHOUT MITK CONVERSION=============================*/
/* ============= edit slice ============*/
int idX = std::abs(ObliquePlaneTestVolumeSize - 59);
int idY = std::abs(ObliquePlaneTestVolumeSize - 23);
int idZ = 0;
int component = 0;
double val = 33.0;
slice->SetScalarComponentFromDouble(idX, idY, idZ, component, val);
mitk::Vector3D indx;
indx[0] = idX;
indx[1] = idY;
indx[2] = idZ;
sliceInMitk->GetGeometry()->IndexToWorld(indx, indx);
/* ============= overwrite slice ============*/
vtkSmartPointer<mitkVtkImageOverwrite> resliceIdx3 = vtkSmartPointer<mitkVtkImageOverwrite>::New();
resliceIdx3->SetOverwriteMode(true);
resliceIdx3->SetInputSlice(slice);
mitk::ExtractSliceFilter::Pointer overwriter3 = mitk::ExtractSliceFilter::New(resliceIdx3);
overwriter3->SetInput(workingImage);
overwriter3->SetWorldGeometry(obliquePlane);
overwriter3->SetVtkOutputRequest(true);
overwriter3->Modified();
overwriter3->Update();
/* ============= check ============*/
areSame = true;
int x = 0;
int y = 0;
int z = 0;
for (x = 0; x < ObliquePlaneTestVolumeSize; ++x)
{
id[0] = x;
for (y = 0; y < ObliquePlaneTestVolumeSize; ++y)
{
id[1] = y;
for (z = 0; z < ObliquePlaneTestVolumeSize; ++z)
{
id[2] = z;
areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id);
if (!areSame)
goto stop3;
}
}
}
stop3:
- MITK_TEST_CONDITION(x == idX && y == z, "overwrited the right index [oblique]");
+ MITK_TEST_CONDITION(x == idX && y == z, "overwrote the right index [oblique]");
}
/*================ #BEGIN test main ================*/
int mitkOverwriteSliceFilterObliquePlaneTest(int, char *[])
{
MITK_TEST_BEGIN("mitkOverwriteSliceFilterObliquePlaneTest")
typedef itk::Image<unsigned short, 3> ImageType;
typedef itk::ImageRegionConstIterator<ImageType> ImageIterator;
ImageType::Pointer image = ImageType::New();
ImageType::IndexType start;
start[0] = start[1] = start[2] = 0;
ImageType::SizeType size;
size[0] = size[1] = size[2] = ObliquePlaneTestVolumeSize;
ImageType::RegionType imgRegion;
imgRegion.SetSize(size);
imgRegion.SetIndex(start);
image->SetRegions(imgRegion);
image->SetSpacing(mitk::Vector(1.0));
image->Allocate();
ImageIterator imageIterator(image, image->GetLargestPossibleRegion());
imageIterator.GoToBegin();
unsigned short pixelValue = 0;
// fill the image with distinct values
while (!imageIterator.IsAtEnd())
{
image->SetPixel(imageIterator.GetIndex(), pixelValue);
++imageIterator;
++pixelValue;
}
/* end setup itk image */
mitk::Image::Pointer refImage;
CastToMitkImage(image, refImage);
mitk::Image::Pointer workingImg;
CastToMitkImage(image, workingImg);
OverwriteObliquePlaneTest(workingImg, refImage);
MITK_TEST_END()
}
diff --git a/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp b/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp
index 50aaef5c35..294223ef94 100644
--- a/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp
+++ b/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp
@@ -1,237 +1,237 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include <mitkDataNode.h>
#include <mitkIOUtil.h>
#include <mitkInteractionTestHelper.h>
#include <mitkLabelSetImage.h>
#include <mitkStandaloneDataStorage.h>
#include <mitkTestFixture.h>
#include <mitkTestingConfig.h>
#include <mitkToolManager.h>
#include <vtkDebugLeaks.h>
class mitkToolInteractionTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkToolInteractionTestSuite);
/// \todo Fix VTK memory leaks. Bug 18098.
vtkDebugLeaks::SetExitError(0);
/* ####### example ######
MITK_TEST(AddToolInteraction_4D_Test);
#########################*/
/// \todo fix crash when wipe tool test is called after another test
// MITK_TEST(WipeToolInteractionTest);
// BUG 19274 - working if recorded again
// MITK_TEST(AddToolInteractionTest);
// MITK_TEST(SubtractToolInteractionTest);
// MITK_TEST(PaintToolInteractionTest);
// MITK_TEST(RegionGrowingToolInteractionTest);
// MITK_TEST(CorrectionToolInteractionTest);
// MITK_TEST(EraseToolInteractionTest);
// MITK_TEST(FillToolInteractionTest);
CPPUNIT_TEST_SUITE_END();
private:
mitk::DataStorage::Pointer m_DataStorage;
mitk::ToolManager::Pointer m_ToolManager;
public:
int GetToolIdByToolName(const std::string &toolName)
{
// find tool from toolname
int numberOfTools = m_ToolManager->GetTools().size();
int toolId = 0;
for (; toolId < numberOfTools; ++toolId)
{
mitk::Tool *currentTool = m_ToolManager->GetToolById(toolId);
if (toolName.compare(currentTool->GetNameOfClass()) == 0)
{
return toolId;
}
}
return -1;
}
void RunTestWithParameters(const std::string &patientImagePath,
const std::string &referenceSegmentationImage,
const std::string &toolName,
const std::string &interactionPattern,
unsigned int timestep = 0,
const std::string &preSegmentationImagePath = std::string())
{
// Create test helper to initialize all necessary objects for interaction
mitk::InteractionTestHelper interactionTestHelper(GetTestDataFilePath(interactionPattern));
// Use data storage of test helper
m_DataStorage = interactionTestHelper.GetDataStorage().GetPointer();
// create ToolManager
m_ToolManager = mitk::ToolManager::New(m_DataStorage);
m_ToolManager->InitializeTools();
m_ToolManager->RegisterClient(); // This is needed because there must be at least one registered. Otherwise tools
// can't be activated.
// Load patient image
mitk::Image::Pointer patientImage = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath(patientImagePath));
CPPUNIT_ASSERT(patientImage.IsNotNull());
mitk::DataNode::Pointer patientImageNode = mitk::DataNode::New();
patientImageNode->SetData(patientImage);
// Activate tool to work with
int toolID = GetToolIdByToolName(toolName);
mitk::Tool *tool = m_ToolManager->GetToolById(toolID);
CPPUNIT_ASSERT(tool != nullptr);
// Create empty segmentation working image
mitk::DataNode::Pointer workingImageNode = mitk::DataNode::New();
const std::string organName = "test";
- mitk::Color color; // actually it dosn't matter which color we are using
+ mitk::Color color; // actually it doesn't matter which color we are using
color.SetRed(1); // but CreateEmptySegmentationNode expects a color parameter
color.SetGreen(0);
color.SetBlue(0);
if (preSegmentationImagePath.empty())
{
workingImageNode = tool->CreateEmptySegmentationNode(patientImage, organName, color);
}
else
{
mitk::Image::Pointer preSegmentation = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath(preSegmentationImagePath));
workingImageNode = tool->CreateSegmentationNode(preSegmentation, organName, color);
}
CPPUNIT_ASSERT(workingImageNode.IsNotNull());
CPPUNIT_ASSERT(workingImageNode->GetData() != nullptr);
// add images to datastorage
interactionTestHelper.AddNodeToStorage(patientImageNode);
interactionTestHelper.AddNodeToStorage(workingImageNode);
// set reference and working image
m_ToolManager->SetWorkingData(workingImageNode);
m_ToolManager->SetReferenceData(patientImageNode);
// set time step
interactionTestHelper.SetTimeStep(timestep);
// load interaction events
m_ToolManager->ActivateTool(toolID);
CPPUNIT_ASSERT(m_ToolManager->GetActiveTool() != nullptr);
// Start Interaction
interactionTestHelper.PlaybackInteraction();
// load reference segmentation image
mitk::Image::Pointer segmentationReferenceImage =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath(referenceSegmentationImage));
mitk::Image::Pointer currentSegmentationImage = mitk::Image::New();
currentSegmentationImage = dynamic_cast<mitk::Image *>(workingImageNode->GetData());
// compare reference with interaction result
MITK_ASSERT_EQUAL(segmentationReferenceImage, currentSegmentationImage, "Reference equals interaction result.");
}
void setUp() {}
void tearDown()
{
m_ToolManager->ActivateTool(-1);
m_ToolManager = nullptr;
}
void AddToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_AddContourTool.nrrd",
"AddContourTool",
"InteractionTestData/Interactions/SegmentationInteractor_AddTool.xml");
}
void SubtractToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_SubtractContourTool.nrrd",
"SubtractContourTool",
"InteractionTestData/Interactions/SegmentationInteractor_SubtractTool.xml");
}
void PaintToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_DrawPaintbrushTool.nrrd",
"DrawPaintbrushTool",
"InteractionTestData/Interactions/SegmentationInteractor_PaintTool.xml");
}
void WipeToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_ErasePaintbrushTool.nrrd",
"ErasePaintbrushTool",
"InteractionTestData/Interactions/SegmentationInteractor_WipeTool.xml");
}
void RegionGrowingToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_RegionGrowingTool.nrrd",
"RegionGrowingTool",
"InteractionTestData/Interactions/SegmentationInteractor_RegionGrowingTool.xml");
}
void CorrectionToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_CorrectorTool2D.nrrd",
"CorrectorTool2D",
"InteractionTestData/Interactions/SegmentationInteractor_CorrectionTool.xml");
}
void EraseToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_EraseRegionTool.nrrd",
"EraseRegionTool",
"InteractionTestData/Interactions/SegmentationInteractor_EraseTool.xml",
0,
"InteractionTestData/ReferenceData/SegmentationInteractor_AddContourTool.nrrd");
}
void FillToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_FillRegionTool.nrrd",
"FillRegionTool",
"InteractionTestData/Interactions/SegmentationInteractor_FillTool.xml",
0,
"InteractionTestData/InputData/SegmentationInteractor_FillTool_input.nrrd");
}
/*############ example ###################
void AddToolInteraction_4D_Test()
{
RunTestWithParameters("US4DCyl.nrrd", "Segmentation/ReferenceSegmentations/AddTool_4D.nrrd", "AddContourTool",
"Segmentation/InteractionPatterns/AddTool_4D.xml", 1);
}
#########################################*/
};
MITK_TEST_SUITE_REGISTRATION(mitkToolInteraction)
diff --git a/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp b/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp
index e737f37410..09bf1f093b 100644
--- a/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp
+++ b/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp
@@ -1,32 +1,32 @@
/*============================================================================
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 "itkLightObject.h"
#include "mitkTestingMacros.h"
#include "mitkToolManagerProvider.h"
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
int mitkToolManagerProviderTest(int, char *[])
{
MITK_TEST_BEGIN("ToolManagerProvider")
// at this point the service is already registered by loading the Segmentation module.
mitk::ToolManagerProvider *service = mitk::ToolManagerProvider::GetInstance();
- MITK_TEST_CONDITION(service != nullptr, "Service was succesfully called");
+ MITK_TEST_CONDITION(service != nullptr, "Service was successfully called");
MITK_TEST_CONDITION((service->GetToolManager()) == (mitk::ToolManagerProvider::GetInstance()->GetToolManager()),
"Service singleton");
MITK_TEST_END()
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h
index 5e14d89449..2d42e7bc3a 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h
@@ -1,63 +1,63 @@
/*============================================================================
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 QmitkBinaryThresholdToolGUIBase_h
#define QmitkBinaryThresholdToolGUIBase_h
#include "QmitkSegWithPreviewToolGUIBase.h"
#include "ctkRangeWidget.h"
#include "ctkSliderWidget.h"
#include <MitkSegmentationUIExports.h>
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief Base GUI for mitk::BinaryThresholdTool.
This GUI shows a slider to change the tool's threshold and an OK button to accept a preview for actual thresholding.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkBinaryThresholdToolGUIBase : public QmitkSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitkBinaryThresholdToolGUIBase, QmitkSegWithPreviewToolGUIBase);
void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat);
void OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper);
protected slots:
void OnThresholdRangeChanged(double min, double max);
void OnThresholdSliderChanged(double value);
protected:
QmitkBinaryThresholdToolGUIBase(bool ulMode);
~QmitkBinaryThresholdToolGUIBase() override;
void DisconnectOldTool(mitk::SegWithPreviewTool* oldTool) override;
void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override;
void InitializeUI(QBoxLayout* mainLayout) override;
void BusyStateChanged(bool) override;
ctkRangeWidget* m_ThresholdRange = nullptr;
ctkSliderWidget* m_ThresholdSlider = nullptr;
/** Indicates if the tool UI is used for a tool with upper an lower threshold (true)
- ore only with one threshold (false)*/
+ or only with one threshold (false)*/
bool m_ULMode;
bool m_InternalUpdate = false;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
index eb2d6ea223..644094ca74 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
@@ -1,1426 +1,1426 @@
/*============================================================================
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 <QmitkMultiLabelInspector.h>
// mitk
#include <mitkRenderingManager.h>
#include <mitkLabelSetImageHelper.h>
#include <mitkDICOMSegmentationPropertyHelper.h>
// Qmitk
#include <QmitkMultiLabelTreeModel.h>
#include <QmitkLabelColorItemDelegate.h>
#include <QmitkLabelToggleItemDelegate.h>
#include <QmitkStyleManager.h>
// Qt
#include <QMenu>
#include <QLabel>
#include <QWidgetAction>
#include <QMessageBox>
#include <QKeyEvent>
#include <ui_QmitkMultiLabelInspectorControls.h>
QmitkMultiLabelInspector::QmitkMultiLabelInspector(QWidget* parent/* = nullptr*/)
: QWidget(parent), m_Controls(new Ui::QmitkMultiLabelInspector), m_SegmentationNodeDataMTime(0)
{
m_Controls->setupUi(this);
m_Model = new QmitkMultiLabelTreeModel(this);
m_Controls->view->setModel(m_Model);
m_ColorItemDelegate = new QmitkLabelColorItemDelegate(this);
auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg"));
auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg"));
m_VisibilityItemDelegate = new QmitkLabelToggleItemDelegate(visibleIcon, invisibleIcon, this);
auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg"));
auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg"));
m_LockItemDelegate = new QmitkLabelToggleItemDelegate(lockIcon, unlockIcon, this);
auto* view = this->m_Controls->view;
view->setItemDelegateForColumn(1, m_LockItemDelegate);
view->setItemDelegateForColumn(2, m_ColorItemDelegate);
view->setItemDelegateForColumn(3, m_VisibilityItemDelegate);
auto* header = view->header();
header->setSectionResizeMode(0,QHeaderView::Stretch);
header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
header->setSectionResizeMode(2, QHeaderView::ResizeToContents);
header->setSectionResizeMode(3, QHeaderView::ResizeToContents);
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_Model, &QAbstractItemModel::modelReset, this, &QmitkMultiLabelInspector::OnModelReset);
connect(m_Model, &QAbstractItemModel::dataChanged, this, &QmitkMultiLabelInspector::OnDataChanged);
connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(OnChangeModelSelection(const QItemSelection&, const QItemSelection&)));
connect(view, &QAbstractItemView::customContextMenuRequested, this, &QmitkMultiLabelInspector::OnContextMenuRequested);
connect(view, &QAbstractItemView::doubleClicked, this, &QmitkMultiLabelInspector::OnItemDoubleClicked);
connect(view, &QAbstractItemView::entered, this, &QmitkMultiLabelInspector::OnEntered);
connect(view, &QmitkMultiLabelTreeView::MouseLeave, this, &QmitkMultiLabelInspector::OnMouseLeave);
}
QmitkMultiLabelInspector::~QmitkMultiLabelInspector()
{
delete m_Controls;
}
void QmitkMultiLabelInspector::Initialize()
{
m_LastValidSelectedLabels = {};
m_ModelManipulationOngoing = false;
m_Model->SetSegmentation(m_Segmentation);
m_Controls->view->expandAll();
m_LastValidSelectedLabels = {};
- //in singel selection mode, if at least one label exist select the first label of the mode.
+ //in single selection mode, if at least one label exist select the first label of the mode.
if (m_Segmentation.IsNotNull() && !this->GetMultiSelectionMode() && m_Segmentation->GetTotalNumberOfLabels() > 0)
{
auto firstIndex = m_Model->FirstLabelInstanceIndex(QModelIndex());
auto labelVariant = firstIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
if (labelVariant.isValid())
{
this->SetSelectedLabel(labelVariant.value<LabelValueType>());
m_Controls->view->selectionModel()->setCurrentIndex(firstIndex, QItemSelectionModel::NoUpdate);
}
}
}
void QmitkMultiLabelInspector::SetMultiSelectionMode(bool multiMode)
{
m_Controls->view->setSelectionMode(multiMode
? QAbstractItemView::SelectionMode::MultiSelection
: QAbstractItemView::SelectionMode::SingleSelection);
}
bool QmitkMultiLabelInspector::GetMultiSelectionMode() const
{
return QAbstractItemView::SelectionMode::MultiSelection == m_Controls->view->selectionMode();
}
void QmitkMultiLabelInspector::SetAllowVisibilityModification(bool visibilityMod)
{
m_AllowVisibilityModification = visibilityMod;
this->m_Model->SetAllowVisibilityModification(visibilityMod);
}
void QmitkMultiLabelInspector::SetAllowLabelModification(bool labelMod)
{
m_AllowLabelModification = labelMod;
}
bool QmitkMultiLabelInspector::GetAllowVisibilityModification() const
{
return m_AllowVisibilityModification;
}
void QmitkMultiLabelInspector::SetAllowLockModification(bool lockMod)
{
m_AllowLockModification = lockMod;
this->m_Model->SetAllowLockModification(lockMod);
}
bool QmitkMultiLabelInspector::GetAllowLockModification() const
{
return m_AllowLockModification;
}
bool QmitkMultiLabelInspector::GetAllowLabelModification() const
{
return m_AllowLabelModification;
}
void QmitkMultiLabelInspector::SetDefaultLabelNaming(bool defaultLabelNaming)
{
m_DefaultLabelNaming = defaultLabelNaming;
}
void QmitkMultiLabelInspector::SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation)
{
if (segmentation != m_Segmentation)
{
m_Segmentation = segmentation;
this->Initialize();
emit SegmentationChanged();
}
}
mitk::LabelSetImage* QmitkMultiLabelInspector::GetMultiLabelSegmentation() const
{
return m_Segmentation;
}
void QmitkMultiLabelInspector::SetMultiLabelNode(mitk::DataNode* node)
{
if (node != this->m_SegmentationNode.GetPointer())
{
m_SegmentationObserver.Reset();
m_SegmentationNode = node;
m_LabelHighlightGuard.SetSegmentationNode(m_SegmentationNode);
m_SegmentationNodeDataMTime = 0;
if (m_SegmentationNode.IsNotNull())
{
auto& widget = *this;
auto checkAndSetSeg = [&widget, node](const itk::EventObject&)
{
if (widget.m_SegmentationNodeDataMTime < node->GetDataReferenceChangedTime())
{
auto newSeg = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
if (nullptr == newSeg) mitkThrow() << "Invalid usage. Node set does not contain a segmentation.";
widget.m_SegmentationNodeDataMTime = node->GetDataReferenceChangedTime();
widget.SetMultiLabelSegmentation(newSeg);
}
};
m_SegmentationObserver.Reset(node, itk::ModifiedEvent(), checkAndSetSeg);
checkAndSetSeg(itk::ModifiedEvent());
}
else
{
this->SetMultiLabelSegmentation(nullptr);
}
}
}
mitk::DataNode* QmitkMultiLabelInspector::GetMultiLabelNode() const
{
return m_SegmentationNode;
}
bool QmitkMultiLabelInspector::GetModelManipulationOngoing() const
{
return m_ModelManipulationOngoing;
}
void QmitkMultiLabelInspector::OnModelReset()
{
m_LastValidSelectedLabels = {};
m_ModelManipulationOngoing = false;
}
void QmitkMultiLabelInspector::OnDataChanged(const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/,
const QList<int>& /*roles*/)
{
if (!m_ModelManipulationOngoing && topLeft.isValid())
m_Controls->view->expand(topLeft);
}
bool EqualLabelSelections(const QmitkMultiLabelInspector::LabelValueVectorType& selection1, const QmitkMultiLabelInspector::LabelValueVectorType& selection2)
{
if (selection1.size() == selection2.size())
{
// lambda to compare node pointer inside both lists
return std::is_permutation(selection1.begin(), selection1.end(), selection2.begin());
}
return false;
}
void QmitkMultiLabelInspector::SetSelectedLabels(const LabelValueVectorType& selectedLabels)
{
if (EqualLabelSelections(this->GetSelectedLabels(), selectedLabels))
{
return;
}
this->UpdateSelectionModel(selectedLabels);
m_LastValidSelectedLabels = selectedLabels;
}
void QmitkMultiLabelInspector::UpdateSelectionModel(const LabelValueVectorType& selectedLabels)
{
// create new selection by retrieving the corresponding indexes of the labels
QItemSelection newCurrentSelection;
for (const auto& labelID : selectedLabels)
{
QModelIndexList matched = m_Model->match(m_Model->index(0, 0), QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole, QVariant(labelID), 1, Qt::MatchRecursive);
if (!matched.empty())
{
newCurrentSelection.select(matched.front(), matched.front());
}
}
m_Controls->view->selectionModel()->select(newCurrentSelection, QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Current);
}
void QmitkMultiLabelInspector::SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel)
{
this->SetSelectedLabels({ selectedLabel });
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetSelectedLabelsFromSelectionModel() const
{
LabelValueVectorType result;
QModelIndexList selectedIndexes = m_Controls->view->selectionModel()->selectedIndexes();
for (const auto& index : std::as_const(selectedIndexes))
{
QVariant qvariantDataNode = m_Model->data(index, QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
if (qvariantDataNode.canConvert<mitk::LabelSetImage::LabelValueType>())
{
result.push_back(qvariantDataNode.value<mitk::LabelSetImage::LabelValueType>());
}
}
return result;
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetSelectedLabels() const
{
return m_LastValidSelectedLabels;
}
mitk::Label* QmitkMultiLabelInspector::GetFirstSelectedLabelObject() const
{
if (m_LastValidSelectedLabels.empty() || m_Segmentation.IsNull())
return nullptr;
return m_Segmentation->GetLabel(m_LastValidSelectedLabels.front());
}
void QmitkMultiLabelInspector::OnChangeModelSelection(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
{
if (!m_ModelManipulationOngoing)
{
auto internalSelection = GetSelectedLabelsFromSelectionModel();
if (internalSelection.empty())
{
//empty selections are not allowed by UI interactions, there should always be at least on label selected.
//but selections are e.g. also cleared if the model is updated (e.g. due to addition of labels)
UpdateSelectionModel(m_LastValidSelectedLabels);
}
else
{
m_LastValidSelectedLabels = internalSelection;
emit CurrentSelectionChanged(GetSelectedLabels());
}
}
}
void QmitkMultiLabelInspector::WaitCursorOn() const
{
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
}
void QmitkMultiLabelInspector::WaitCursorOff() const
{
this->RestoreOverrideCursor();
}
void QmitkMultiLabelInspector::RestoreOverrideCursor() const
{
QApplication::restoreOverrideCursor();
}
mitk::Label* QmitkMultiLabelInspector::GetCurrentLabel() const
{
auto currentIndex = this->m_Controls->view->currentIndex();
auto labelVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelDataRole);
mitk::Label::Pointer currentIndexLabel = nullptr;
if (labelVariant.isValid())
{
auto uncastedLabel = labelVariant.value<void*>();
currentIndexLabel = static_cast<mitk::Label*>(uncastedLabel);
}
return currentIndexLabel;
}
QmitkMultiLabelInspector::IndexLevelType QmitkMultiLabelInspector::GetCurrentLevelType() const
{
auto currentIndex = this->m_Controls->view->currentIndex();
auto labelInstanceVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceDataRole);
auto labelVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelDataRole);
if (labelInstanceVariant.isValid() )
{
return IndexLevelType::LabelInstance;
}
else if (labelVariant.isValid())
{
return IndexLevelType::LabelClass;
}
return IndexLevelType::Group;
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetCurrentlyAffactedLabelInstances() const
{
auto currentIndex = m_Controls->view->currentIndex();
return m_Model->GetLabelsInSubTree(currentIndex);
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetLabelInstancesOfSelectedFirstLabel() const
{
if (m_Segmentation.IsNull())
return {};
if (this->GetSelectedLabels().empty())
return {};
const auto index = m_Model->indexOfLabel(this->GetSelectedLabels().front());
return m_Model->GetLabelInstancesOfSameLabelClass(index);
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabelInstanceInternal(mitk::Label* templateLabel)
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabelInstance.";
if (nullptr == templateLabel)
mitkThrow() << "QmitkMultiLabelInspector is in an invalid state. AddNewLabelInstanceInternal was called with a non existing label as template";
auto groupID = m_Segmentation->GetGroupIndexOfLabel(templateLabel->GetValue());
m_ModelManipulationOngoing = true;
auto newLabel = m_Segmentation->AddLabel(templateLabel, groupID, true);
m_ModelManipulationOngoing = false;
this->SetSelectedLabel(newLabel->GetValue());
auto index = m_Model->indexOfLabel(newLabel->GetValue());
if (index.isValid())
{
m_Controls->view->expand(index.parent());
}
else
{
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabel->GetValue();
}
emit ModelUpdated();
return newLabel;
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabelInstance()
{
auto currentLabel = this->GetFirstSelectedLabelObject();
if (nullptr == currentLabel)
return nullptr;
auto result = this->AddNewLabelInstanceInternal(currentLabel);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
return result;
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup)
{
auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(m_Segmentation);
bool canceled = false;
if (!m_DefaultLabelNaming)
emit LabelRenameRequested(newLabel, false, canceled);
if (canceled) return nullptr;
m_ModelManipulationOngoing = true;
m_Segmentation->AddLabel(newLabel, containingGroup, false);
m_ModelManipulationOngoing = false;
this->SetSelectedLabel(newLabel->GetValue());
auto index = m_Model->indexOfLabel(newLabel->GetValue());
if (!index.isValid())
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the "
"model after adding it to the segmentation. Label value: " << newLabel->GetValue();
m_Controls->view->expand(index.parent());
emit ModelUpdated();
return newLabel;
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabel()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabel.";
if (m_Segmentation.IsNull())
{
return nullptr;
}
auto currentLabel = this->GetFirstSelectedLabelObject();
mitk::LabelSetImage::GroupIndexType groupID = nullptr != currentLabel
? m_Segmentation->GetGroupIndexOfLabel(currentLabel->GetValue())
: 0;
auto result = AddNewLabelInternal(groupID);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
return result;
}
void QmitkMultiLabelInspector::DeleteLabelInstance()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabelInstance.";
if (m_Segmentation.IsNull())
return;
auto label = this->GetFirstSelectedLabelObject();
if (nullptr == label)
return;
auto index = m_Model->indexOfLabel(label->GetValue());
auto instanceName = index.data(Qt::DisplayRole);
auto question = "Do you really want to delete label instance \"" + instanceName.toString() + "\"?";
auto answer = QMessageBox::question(this, QString("Delete label instances"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answer == QMessageBox::Yes)
{
this->DeleteLabelInternal({ label->GetValue() });
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::DeleteLabel()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabel.";
if (m_Segmentation.IsNull())
return;
const auto label = this->GetFirstSelectedLabelObject();
if (nullptr == label)
return;
const auto relevantLabels = this->GetLabelInstancesOfSelectedFirstLabel();
if (relevantLabels.empty())
return;
auto question = "Do you really want to delete label \"" + QString::fromStdString(label->GetName());
question = relevantLabels.size()==1 ? question + "\"?" : question + "\" with all "+QString::number(relevantLabels.size()) +" instances?";
auto answer = QMessageBox::question(this, QString("Delete label"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answer == QMessageBox::Yes)
{
this->DeleteLabelInternal(relevantLabels);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::DeleteLabelInternal(const LabelValueVectorType& labelValues)
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabelInternal.";
if (m_Segmentation.IsNull())
{
return;
}
QVariant nextLabelVariant;
this->WaitCursorOn();
m_ModelManipulationOngoing = true;
for (auto labelValue : labelValues)
{
if (labelValue == labelValues.back())
{
auto currentIndex = m_Model->indexOfLabel(labelValue);
auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex);
nextLabelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
}
m_Segmentation->RemoveLabel(labelValue);
}
m_ModelManipulationOngoing = false;
this->WaitCursorOff();
if (nextLabelVariant.isValid())
{
auto newLabelValue = nextLabelVariant.value<LabelValueType>();
this->SetSelectedLabel(newLabelValue);
auto index = m_Model->indexOfLabel(newLabelValue); //we have to get index again, because it could have changed due to remove operation.
if (index.isValid())
{
m_Controls->view->expand(index.parent());
}
else
{
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue;
}
}
else
{
this->SetSelectedLabels({});
}
emit ModelUpdated();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
mitk::Label* QmitkMultiLabelInspector::AddNewGroup()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabel.";
if (m_Segmentation.IsNull())
{
return nullptr;
}
mitk::LabelSetImage::GroupIndexType groupID = 0;
mitk::Label* newLabel = nullptr;
m_ModelManipulationOngoing = true;
try
{
this->WaitCursorOn();
groupID = m_Segmentation->AddLayer();
m_Segmentation->SetActiveLayer(groupID);
this->WaitCursorOff();
newLabel = this->AddNewLabelInternal(groupID);
}
catch (mitk::Exception& e)
{
this->WaitCursorOff();
m_ModelManipulationOngoing = false;
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Add group", "Could not add a new group. See error log for details.");
}
m_ModelManipulationOngoing = false;
emit ModelUpdated();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
return newLabel;
}
void QmitkMultiLabelInspector::RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID)
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
return;
if (m_Segmentation->GetNumberOfLayers() < 2)
return;
auto currentIndex = m_Model->indexOfGroup(groupID);
auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex);
auto labelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
try
{
this->WaitCursorOn();
m_ModelManipulationOngoing = true;
m_Segmentation->RemoveGroup(groupID);
m_ModelManipulationOngoing = false;
this->WaitCursorOff();
}
catch (mitk::Exception& e)
{
m_ModelManipulationOngoing = false;
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Delete group", "Could not delete the currently active group. See error log for details.");
return;
}
if (labelVariant.isValid())
{
auto newLabelValue = labelVariant.value<LabelValueType>();
this->SetSelectedLabel(newLabelValue);
auto index = m_Model->indexOfLabel(newLabelValue); //we have to get index again, because it could have changed due to remove operation.
if (index.isValid())
{
m_Controls->view->expand(index.parent());
}
else
{
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue;
}
}
else
{
this->SetSelectedLabels({});
}
emit ModelUpdated();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkMultiLabelInspector::RemoveGroup()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
return;
if (m_Segmentation->GetNumberOfLayers() < 2)
{
QMessageBox::information(this, "Delete group", "Cannot delete last remaining group. A segmentation must contain at least a single group.");
return;
}
const auto* selectedLabel = this->GetFirstSelectedLabelObject();
if (selectedLabel == nullptr)
return;
const auto groupID = m_Segmentation->GetGroupIndexOfLabel(selectedLabel->GetValue());
auto groupName = QString::fromStdString(mitk::LabelSetImageHelper::CreateDisplayGroupName(m_Segmentation, groupID));
auto question = QStringLiteral("Do you really want to delete group \"%1\" including all of its labels?").arg(groupName);
auto answer = QMessageBox::question(this, QString("Delete group \"%1\"").arg(groupName), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (answer != QMessageBox::Yes)
return;
this->RemoveGroupInternal(groupID);
}
void QmitkMultiLabelInspector::OnDeleteGroup()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
return;
auto currentIndex = this->m_Controls->view->currentIndex();
auto groupIDVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::GroupIDRole);
if (groupIDVariant.isValid())
{
auto groupID = groupIDVariant.value<mitk::LabelSetImage::GroupIndexType>();
auto groupName = QString::fromStdString(mitk::LabelSetImageHelper::CreateDisplayGroupName(m_Segmentation, groupID));
auto question = QStringLiteral("Do you really want to delete group \"%1\" including all of its labels?").arg(groupName);
auto answer = QMessageBox::question(this, QString("Delete group \"%1\"").arg(groupName), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (answer != QMessageBox::Yes)
return;
this->RemoveGroupInternal(groupID);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
};
void QmitkMultiLabelInspector::OnContextMenuRequested(const QPoint& /*pos*/)
{
if (m_Segmentation.IsNull() || !this->isEnabled())
return;
const auto indexLevel = this->GetCurrentLevelType();
auto currentIndex = this->m_Controls->view->currentIndex();
//this ensures correct highlighting is the context menu is triggered while
//another context menu is already open.
if (currentIndex.isValid() && this->m_AboutToShowContextMenu) this->OnEntered(this->m_Controls->view->currentIndex());
QMenu* menu = new QMenu(this);
if (IndexLevelType::Group == indexLevel)
{
if (m_AllowLabelModification)
{
QAction* addInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add.svg")), "&Add label", this);
QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabel);
menu->addAction(addInstanceAction);
if (m_Segmentation->GetNumberOfLayers() > 1)
{
QAction* removeAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_group_delete.svg")), "Delete group", this);
QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnDeleteGroup);
menu->addAction(removeAction);
}
}
if (m_AllowLockModification)
{
menu->addSeparator();
QAction* lockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")), "Lock group", this);
QObject::connect(lockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnLockAffectedLabels);
menu->addAction(lockAllAction);
QAction* unlockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")), "Unlock group", this);
QObject::connect(unlockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnUnlockAffectedLabels);
menu->addAction(unlockAllAction);
}
if (m_AllowVisibilityModification)
{
menu->addSeparator();
QAction* viewAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Show group", this);
QObject::connect(viewAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsVisible);
menu->addAction(viewAllAction);
QAction* hideAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")), "Hide group", this);
QObject::connect(hideAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible);
menu->addAction(hideAllAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr != opacityAction)
menu->addAction(opacityAction);
}
}
else if (IndexLevelType::LabelClass == indexLevel)
{
if (m_AllowLabelModification)
{
QAction* addInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add_instance.svg")), "Add label instance", this);
QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabelInstance);
menu->addAction(addInstanceAction);
QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label", this);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
QAction* removeAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete.svg")), "&Delete label", this);
QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnDeleteAffectedLabel);
menu->addAction(removeAction);
}
if (m_AllowLockModification)
{
menu->addSeparator();
QAction* lockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")), "Lock label instances", this);
QObject::connect(lockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnLockAffectedLabels);
menu->addAction(lockAllAction);
QAction* unlockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")), "Unlock label instances", this);
QObject::connect(unlockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnUnlockAffectedLabels);
menu->addAction(unlockAllAction);
}
if (m_AllowVisibilityModification)
{
menu->addSeparator();
QAction* viewAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Show label instances", this);
QObject::connect(viewAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsVisible);
menu->addAction(viewAllAction);
QAction* hideAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")), "Hide label instances", this);
QObject::connect(hideAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible);
menu->addAction(hideAllAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr!=opacityAction)
menu->addAction(opacityAction);
}
}
else
{
auto selectedLabelValues = this->GetSelectedLabels();
if (selectedLabelValues.empty())
return;
if (this->GetMultiSelectionMode() && selectedLabelValues.size() > 1)
{
if (m_AllowLabelModification)
{
QAction* mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this);
QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool)));
menu->addAction(mergeAction);
QAction* removeLabelsAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete_instance.svg")), "&Delete selected labels", this);
QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnDeleteLabels(bool)));
menu->addAction(removeLabelsAction);
QAction* clearLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "&Clear selected labels", this);
QObject::connect(clearLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnClearLabels(bool)));
menu->addAction(clearLabelsAction);
}
if (m_AllowVisibilityModification)
{
if (m_AllowLabelModification) menu->addSeparator();
QAction* viewOnlyAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Hide everything but this", this);
QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
menu->addAction(viewOnlyAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr != opacityAction)
menu->addAction(opacityAction);
}
}
else
{
if (m_AllowLabelModification)
{
QAction* addInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add_instance.svg")), "&Add label instance", this);
QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabelInstance);
menu->addAction(addInstanceAction);
const auto selectedLabelIndex = m_Model->indexOfLabel(selectedLabelValues.front());
if (m_Model->GetLabelInstancesOfSameLabelClass(selectedLabelIndex).size() > 1) // Only labels that actually appear as instance (having additional instances)
{
QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label instance", this);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
QAction* removeInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete_instance.svg")), "&Delete label instance", this);
QObject::connect(removeInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::DeleteLabelInstance);
menu->addAction(removeInstanceAction);
}
else
{
QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label", this);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
}
QAction* removeLabelAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete.svg")), "Delete &label", this);
QObject::connect(removeLabelAction, &QAction::triggered, this, &QmitkMultiLabelInspector::DeleteLabel);
menu->addAction(removeLabelAction);
QAction* clearAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "&Clear content", this);
QObject::connect(clearAction, SIGNAL(triggered(bool)), this, SLOT(OnClearLabel(bool)));
menu->addAction(clearAction);
}
if (m_AllowVisibilityModification)
{
if (m_AllowLabelModification) menu->addSeparator();
QAction* viewOnlyAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Hide everything but this", this);
QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
menu->addAction(viewOnlyAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr != opacityAction)
menu->addAction(opacityAction);
}
}
}
QObject::connect(menu, &QMenu::aboutToHide, this, &QmitkMultiLabelInspector::OnMouseLeave);
m_AboutToShowContextMenu = true;
menu->popup(QCursor::pos());
}
QWidgetAction* QmitkMultiLabelInspector::CreateOpacityAction()
{
auto selectedLabelValues = this->GetSelectedLabels();
auto relevantLabelValues = !this->GetMultiSelectionMode() || selectedLabelValues.size() <= 1
? this->GetCurrentlyAffactedLabelInstances()
: selectedLabelValues;
std::vector<mitk::Label*> relevantLabels;
if (!relevantLabelValues.empty())
{
for (auto value : relevantLabelValues)
{
auto label = this->m_Segmentation->GetLabel(value);
if (label.IsNull())
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
relevantLabels.emplace_back(label);
}
auto* opacitySlider = new QSlider;
opacitySlider->setMinimum(0);
opacitySlider->setMaximum(100);
opacitySlider->setOrientation(Qt::Horizontal);
auto opacity = relevantLabels.front()->GetOpacity();
opacitySlider->setValue(static_cast<int>(opacity * 100));
auto segmentation = m_Segmentation;
auto guard = &m_LabelHighlightGuard;
auto onChangeLambda = [segmentation, relevantLabels](const int value)
{
auto opacity = static_cast<float>(value) / 100.0f;
for (auto label : relevantLabels)
{
label->SetOpacity(opacity);
segmentation->UpdateLookupTable(label->GetValue());
}
segmentation->GetLookupTable()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
};
QObject::connect(opacitySlider, &QSlider::valueChanged, this, onChangeLambda);
auto onPressedLambda = [guard]()
{
guard->SetHighlightedLabels({});
};
QObject::connect(opacitySlider, &QSlider::sliderPressed, this, onPressedLambda);
auto onReleasedLambda = [relevantLabelValues, guard]()
{
guard->SetHighlightedLabels(relevantLabelValues);
};
QObject::connect(opacitySlider, &QSlider::sliderReleased, this, onReleasedLambda);
QLabel* opacityLabel = new QLabel("Opacity: ");
QVBoxLayout* opacityWidgetLayout = new QVBoxLayout;
opacityWidgetLayout->setContentsMargins(4, 4, 4, 4);
opacityWidgetLayout->addWidget(opacityLabel);
opacityWidgetLayout->addWidget(opacitySlider);
QWidget* opacityWidget = new QWidget;
opacityWidget->setLayout(opacityWidgetLayout);
QWidgetAction* opacityAction = new QWidgetAction(this);
opacityAction->setDefaultWidget(opacityWidget);
return opacityAction;
}
return nullptr;
}
void QmitkMultiLabelInspector::OnClearLabels(bool /*value*/)
{
QString question = "Do you really want to clear the selected labels?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Clear selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->EraseLabels(this->GetSelectedLabels());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
}
void QmitkMultiLabelInspector::OnDeleteAffectedLabel()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
{
return;
}
auto affectedLabels = GetCurrentlyAffactedLabelInstances();
auto currentLabel = m_Segmentation->GetLabel(affectedLabels.front());
if (currentLabel.IsNull())
{
MITK_WARN << "Ignore operation. Try to delete non-existing label. Invalid ID: " << affectedLabels.front();
return;
}
QString question = "Do you really want to delete all instances of label \"" + QString::fromStdString(currentLabel->GetName()) + "\"?";
QMessageBox::StandardButton answerButton =
QMessageBox::question(this, "Delete label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->DeleteLabelInternal(affectedLabels);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnDeleteLabels(bool /*value*/)
{
QString question = "Do you really want to remove the selected labels?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->RemoveLabels(this->GetSelectedLabels());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnMergeLabels(bool /*value*/)
{
auto currentLabel = GetCurrentLabel();
QString question = "Do you really want to merge selected labels into \"" + QString::fromStdString(currentLabel->GetName())+"\"?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->MergeLabels(currentLabel->GetValue(), this->GetSelectedLabels());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnAddLabel()
{
auto currentIndex = this->m_Controls->view->currentIndex();
auto groupIDVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::GroupIDRole);
if (groupIDVariant.isValid())
{
auto groupID = groupIDVariant.value<mitk::LabelSetImage::GroupIndexType>();
this->AddNewLabelInternal(groupID);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnAddLabelInstance()
{
auto currentLabel = this->GetCurrentLabel();
if (nullptr == currentLabel)
return;
this->AddNewLabelInstanceInternal(currentLabel);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelInspector::OnClearLabel(bool /*value*/)
{
auto currentLabel = GetFirstSelectedLabelObject();
QString question = "Do you really want to clear the contents of label \"" + QString::fromStdString(currentLabel->GetName())+"\"?";
QMessageBox::StandardButton answerButton =
QMessageBox::question(this, "Clear label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->EraseLabel(currentLabel->GetValue());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
}
void QmitkMultiLabelInspector::OnRenameLabel(bool /*value*/)
{
auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
auto currentLabel = this->GetCurrentLabel();
bool canceled = false;
emit LabelRenameRequested(currentLabel, true, canceled);
if (canceled) return;
for (auto value : relevantLabelValues)
{
if (value != currentLabel->GetValue())
{
auto label = this->m_Segmentation->GetLabel(value);
if (nullptr == label)
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
label->SetName(currentLabel->GetName());
label->SetColor(currentLabel->GetColor());
m_Segmentation->UpdateLookupTable(label->GetValue());
m_Segmentation->GetLookupTable()->Modified();
mitk::DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
emit ModelUpdated();
}
void QmitkMultiLabelInspector::SetLockOfAffectedLabels(bool locked) const
{
auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
if (!relevantLabelValues.empty())
{
for (auto value : relevantLabelValues)
{
auto label = this->m_Segmentation->GetLabel(value);
if (label.IsNull())
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
label->SetLocked(locked);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelInspector::OnUnlockAffectedLabels()
{
this->SetLockOfAffectedLabels(false);
}
void QmitkMultiLabelInspector::OnLockAffectedLabels()
{
this->SetLockOfAffectedLabels(true);
}
void QmitkMultiLabelInspector::SetVisibilityOfAffectedLabels(bool visible) const
{
auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
if (!relevantLabelValues.empty())
{
for (auto value : relevantLabelValues)
{
auto label = this->m_Segmentation->GetLabel(value);
if (label.IsNull())
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
label->SetVisible(visible);
m_Segmentation->UpdateLookupTable(label->GetValue());
}
m_Segmentation->GetLookupTable()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelInspector::OnSetAffectedLabelsVisible()
{
this->SetVisibilityOfAffectedLabels(true);
}
void QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible()
{
this->SetVisibilityOfAffectedLabels(false);
}
void QmitkMultiLabelInspector::OnSetOnlyActiveLabelVisible(bool /*value*/)
{
auto selectedLabelValues = this->GetSelectedLabels();
if (selectedLabelValues.empty()) return;
m_Segmentation->SetAllLabelsVisible(false);
for (auto selectedValue : selectedLabelValues)
{
auto currentLabel = m_Segmentation->GetLabel(selectedValue);
currentLabel->SetVisible(true);
m_Segmentation->UpdateLookupTable(selectedValue);
}
m_Segmentation->GetLookupTable()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
this->PrepareGoToLabel(selectedLabelValues.front());
}
void QmitkMultiLabelInspector::OnItemDoubleClicked(const QModelIndex& index)
{
if (!index.isValid()) return;
if (index.column() > 0) return;
auto labelVariant = index.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
if (!labelVariant.isValid()) return;
const auto labelID = labelVariant.value<mitk::Label::PixelType>();
if (QApplication::queryKeyboardModifiers().testFlag(Qt::AltModifier))
{
this->OnRenameLabel(false);
return;
}
this->PrepareGoToLabel(labelID);
}
void QmitkMultiLabelInspector::PrepareGoToLabel(mitk::Label::PixelType labelID) const
{
this->WaitCursorOn();
m_Segmentation->UpdateCenterOfMass(labelID);
this->WaitCursorOff();
const auto currentLabel = m_Segmentation->GetLabel(labelID);
if (currentLabel.IsNull())
return;
const mitk::Point3D& pos = currentLabel->GetCenterOfMassCoordinates();
if (pos.GetVnlVector().max_value() > 0.0)
{
emit GoToLabel(currentLabel->GetValue(), pos);
}
}
void QmitkMultiLabelInspector::OnEntered(const QModelIndex& index)
{
if (m_SegmentationNode.IsNotNull())
{
auto labelVariant = index.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
auto highlightedValues = m_Model->GetLabelsInSubTree(index);
m_LabelHighlightGuard.SetHighlightedLabels(highlightedValues);
}
m_AboutToShowContextMenu = false;
}
void QmitkMultiLabelInspector::OnMouseLeave()
{
if (m_SegmentationNode.IsNotNull() && !m_AboutToShowContextMenu)
{
m_LabelHighlightGuard.SetHighlightedLabels({});
}
else
{
m_AboutToShowContextMenu = false;
}
}
void QmitkMultiLabelInspector::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Shift)
{
m_LabelHighlightGuard.SetHighlightInvisibleLabels(true);
}
QWidget::keyPressEvent(event);
}
void QmitkMultiLabelInspector::keyReleaseEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Shift)
{
m_LabelHighlightGuard.SetHighlightInvisibleLabels(false);
}
QWidget::keyPressEvent(event);
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
index 85defad2c0..d2a7c44028 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
@@ -1,339 +1,339 @@
/*============================================================================
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 QmitkMultiLabelInspector_h
#define QmitkMultiLabelInspector_h
#include <MitkSegmentationUIExports.h>
#include <mitkWeakPointer.h>
#include <mitkLabelSetImage.h>
#include <mitkDataNode.h>
#include <mitkLabelHighlightGuard.h>
#include <QWidget>
#include <QItemSelectionModel>
class QmitkMultiLabelTreeModel;
class QStyledItemDelegate;
class QWidgetAction;
namespace Ui
{
class QmitkMultiLabelInspector;
}
/*
* @brief This is an inspector that offers a tree view on the labels and groups of a MultiLabelSegmentation instance.
-* It also allows some manipulation operations an the labels/groups accordin to the UI/selection state.
+* It also allows some manipulation operations an the labels/groups according to the UI/selection state.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelInspector : public QWidget
{
Q_OBJECT
public:
QmitkMultiLabelInspector(QWidget* parent = nullptr);
~QmitkMultiLabelInspector();
bool GetMultiSelectionMode() const;
bool GetAllowVisibilityModification() const;
bool GetAllowLockModification() const;
bool GetAllowLabelModification() const;
- /** Indicates if the inspector is currently modifiying the model/segmentation.
+ /** Indicates if the inspector is currently modifying the model/segmentation.
Thus as long as the manipulation is ongoing, one should assume the model to be in an invalid state.*/
bool GetModelManipulationOngoing() const;
using LabelValueType = mitk::LabelSetImage::LabelValueType;
using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType;
/**
* @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values).
*/
LabelValueVectorType GetSelectedLabels() const;
/** @brief Returns the label that currently has the focus in the tree view.
*
* The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line.
*
* The current label must not equal the selected label(s). If the mouse is not hovering above a label
* (label class or instance item), the method will return nullptr.
*/
mitk::Label* GetCurrentLabel() const;
enum class IndexLevelType
{
Group,
LabelClass,
LabelInstance
};
/** @brief Returns the level of the index that currently has the focus in the tree view.
*
* The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line.
*/
IndexLevelType GetCurrentLevelType() const;
/** @brief Returns all label values that are currently affected.
*
* Affected means that these labels (including the one returned by GetCurrentLabel) are in the subtree of the tree
* view element that currently has the focus (indicated by QTreeView::currentIndex, thus the mouse is over it and
* it has a dashed border line.
*/
LabelValueVectorType GetCurrentlyAffactedLabelInstances() const;
/** @brief Returns the values of all label instances that are of the same label (class) like the first selected label instance.
*
* If no label is selected an empty vector will be returned.
*/
LabelValueVectorType GetLabelInstancesOfSelectedFirstLabel() const;
Q_SIGNALS:
/**
* @brief A signal that will be emitted if the selected labels change.
*
* @param labels A list of label values that are now selected.
*/
void CurrentSelectionChanged(LabelValueVectorType labels) const;
/**
* @brief A signal that will be emitted if the user has requested to "go to" a certain label.
*
* Going to a label would be e.g. to focus the renderwindows on the centroid of the label.
* @param label The label that should be focused.
* @param point in World coordinate that should be focused.
*/
void GoToLabel(LabelValueType label, const mitk::Point3D& point) const;
/** @brief Signal that is emitted, if a label should be (re)named and default
* label naming is deactivated.
*
* The instance for which a new name is requested is passed with the signal.
* @param label Pointer to the instance that needs a (new) name.
* @param rename Indicates if it is a renaming or naming of a new label.
* @param [out] canceled Indicating if the request was canceled by the used.
*/
void LabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
/** @brief Signal that is emitted, if the model was updated (e.g. by a delete or add operation).*/
void ModelUpdated() const;
/** @brief Signal is emitted, if the segmentation is changed that is observed by the inspector.*/
void SegmentationChanged() const;
public Q_SLOTS:
/**
* @brief Transform a list of label values into the new selection of the inspector.
* @param selectedLabels A list of selected label values.
* @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers
* should regard that to avoid signal loops.
*/
void SetSelectedLabels(const LabelValueVectorType& selectedLabels);
/**
* @brief The passed label will be used as new selection in the widget
* @param selectedLabel Value of the selected label.
* @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers
* should regard that to avoid signal loops.
*/
void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel);
/** @brief Sets the segmentation that will be used and monitored by the widget.
* @param segmentation A pointer to the segmentation to set.
* @remark You cannot set the segmentation directly if a segmentation node is
* also set. Reset the node (nullptr) if you want to change to direct segmentation
* setting.
* @pre Segmentation node is nullptr.
*/
void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation);
mitk::LabelSetImage* GetMultiLabelSegmentation() const;
/**
* @brief Sets the segmentation node that will be used /monitored by the widget.
*
* @param node A pointer to the segmentation node.
* @remark If not set some features (e.g. highlighting in render windows) of the inspectors
* are not active.
* @remark Currently it is also needed to circumvent the fact that
* modification of data does not directly trigger modification of the
* node (see T27307).
*/
void SetMultiLabelNode(mitk::DataNode* node);
mitk::DataNode* GetMultiLabelNode() const;
void SetMultiSelectionMode(bool multiMode);
void SetAllowVisibilityModification(bool visiblityMod);
void SetAllowLockModification(bool lockMod);
void SetAllowLabelModification(bool labelMod);
void SetDefaultLabelNaming(bool defaultLabelNaming);
/** @brief Adds an instance of the same label/class like the first label instance
* indicated by GetSelectedLabels() to the segmentation.
*
* This new label instance is returned by the function. If the inspector has no selected label,
* no new instance will be generated and nullptr will be returned.
*
* @remark The new label instance is a clone of the selected label instance.
* Therefore all properties but the LabelValue will be the same.
*
* @pre AllowLabeModification must be set to true.
*/
mitk::Label* AddNewLabelInstance();
/** @brief Adds a new label to the segmentation.
* Depending on the settings the name of
* the label will be either default generated or the rename delegate will be used. The label
* will be added to the same group as the first currently selected label.
*
* @pre AllowLabeModification must be set to true.*/
mitk::Label* AddNewLabel();
/** @brief Removes the first currently selected label instance of the segmentation.
* If no label is selected
* nothing will happen.
*
* @pre AllowLabeModification must be set to true.*/
void DeleteLabelInstance();
/** @brief Delete the first currently selected label and all its instances of the segmentation.
* If no label is selected
* nothing will happen.
*
* @pre AllowLabeModification must be set to true.*/
void DeleteLabel();
/** @brief Adds a new group with a new label to segmentation.
*
* @pre AllowLabeModification must be set to true.*/
mitk::Label* AddNewGroup();
/** @brief Removes the group of the first currently selected label of the segmentation.
*If no label is selected nothing will happen.
*
* @pre AllowLabeModification must be set to true.*/
void RemoveGroup();
void SetVisibilityOfAffectedLabels(bool visible) const;
void SetLockOfAffectedLabels(bool visible) const;
protected:
void Initialize();
void OnModelReset();
void OnDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight,
const QList<int>& roles = QList<int>());
QmitkMultiLabelTreeModel* m_Model;
mitk::LabelSetImage::Pointer m_Segmentation;
LabelValueVectorType m_LastValidSelectedLabels;
QStyledItemDelegate* m_LockItemDelegate;
QStyledItemDelegate* m_ColorItemDelegate;
QStyledItemDelegate* m_VisibilityItemDelegate;
Ui::QmitkMultiLabelInspector* m_Controls;
LabelValueVectorType GetSelectedLabelsFromSelectionModel() const;
void UpdateSelectionModel(const LabelValueVectorType& selectedLabels);
/** @brief Helper that returns the label object (if multiple labels are selected the first).
*/
mitk::Label* GetFirstSelectedLabelObject() const;
mitk::Label* AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup);
/**@brief Adds an instance of the same label/class like the passed label value
*/
mitk::Label* AddNewLabelInstanceInternal(mitk::Label* templateLabel);
void RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID);
void DeleteLabelInternal(const LabelValueVectorType& labelValues);
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
private Q_SLOTS:
/** @brief Transform a labels selection into a data node list and emit the 'CurrentSelectionChanged'-signal.
*
* The function adds the selected nodes from the original selection that could not be modified, if
* m_SelectOnlyVisibleNodes is false.
* This slot is internally connected to the 'selectionChanged'-signal of the selection model of the private member item view.
*
* @param selected The newly selected items.
* @param deselected The newly deselected items.
*/
void OnChangeModelSelection(const QItemSelection& selected, const QItemSelection& deselected);
void OnContextMenuRequested(const QPoint&);
void OnAddLabel();
void OnAddLabelInstance();
void OnDeleteGroup();
void OnDeleteAffectedLabel();
void OnDeleteLabels(bool);
void OnClearLabels(bool);
void OnMergeLabels(bool);
void OnRenameLabel(bool);
void OnClearLabel(bool);
void OnUnlockAffectedLabels();
void OnLockAffectedLabels();
void OnSetAffectedLabelsVisible();
void OnSetAffectedLabelsInvisible();
void OnSetOnlyActiveLabelVisible(bool);
void OnItemDoubleClicked(const QModelIndex& index);
void WaitCursorOn() const;
void WaitCursorOff() const;
void RestoreOverrideCursor() const;
void PrepareGoToLabel(LabelValueType labelID) const;
void OnEntered(const QModelIndex& index);
void OnMouseLeave();
QWidgetAction* CreateOpacityAction();
private:
bool m_ShowVisibility = true;
bool m_ShowLock = true;
bool m_ShowOther = false;
- /** @brief Indicates if the context menu allows changes in visiblity.
+ /** @brief Indicates if the context menu allows changes in visibility.
*
- * Visiblity includes also color
+ * Visibility includes also color
*/
bool m_AllowVisibilityModification = true;
/** @brief Indicates if the context menu allows changes in lock state.
*/
bool m_AllowLockModification = true;
/** @brief Indicates if the context menu allows label modifications (adding, removing, renaming ...)
*/
bool m_AllowLabelModification = false;
bool m_DefaultLabelNaming = true;
bool m_ModelManipulationOngoing = false;
bool m_AboutToShowContextMenu = false;
mitk::DataNode::Pointer m_SegmentationNode;
unsigned long m_SegmentationNodeDataMTime;
mitk::ITKEventObserverGuard m_SegmentationObserver;
mitk::LabelHighlightGuard m_LabelHighlightGuard;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
index d0764c1e4e..54b87bc561 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
@@ -1,198 +1,198 @@
/*============================================================================
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 QmitkMultiLabelManager_h
#define QmitkMultiLabelManager_h
#include <MitkSegmentationUIExports.h>
#include <mitkLabelSetImage.h>
#include <mitkDataNode.h>
#include <mitkNumericTypes.h>
#include <mitkITKEventObserverGuard.h>
#include <QWidget>
class QmitkDataStorageComboBox;
class QCompleter;
namespace Ui
{
class QmitkMultiLabelManagerControls;
}
namespace mitk
{
class DataStorage;
}
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelManager : public QWidget
{
Q_OBJECT
public:
explicit QmitkMultiLabelManager(QWidget *parent = nullptr);
~QmitkMultiLabelManager() override;
using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType;
/**
* @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values).
*/
LabelValueVectorType GetSelectedLabels() const;
mitk::LabelSetImage* GetMultiLabelSegmentation() const;
mitk::DataNode* GetMultiLabelNode() const;
Q_SIGNALS:
/**
* @brief A signal that will be emitted if the selected labels change.
*
* @param labels A list of label values that are now selected.
*/
void CurrentSelectionChanged(const LabelValueVectorType& labels);
/**
* @brief A signal that will be emitted if the user has requested to "go to" a certain label.
*
* Going to a label would be e.g. to focus the render windows on the centroid of the label.
* @param label The label that should be focused.
* @param point in World coordinate that should be focused.
*/
void GoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& point) const;
/** @brief Signal that is emitted, if a label should be (re)named and default
* label naming is deactivated.
*
* The instance for which a new name is requested is passed with the signal.
* @param label Pointer to the instance that needs a (new) name.
* @param [out] canceled Indicating if the request was canceled by the used.
*/
void LabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
public Q_SLOTS:
/**
* @brief Transform a list label values into a model selection and set this as a new selection of the view
*
* @param selectedLabels A list of data nodes that should be newly selected.
*/
void SetSelectedLabels(const LabelValueVectorType& selectedLabels);
/**
* @brief Selects the passed label instance and sets a new selection of the view
*
* @param selectedLabel Value of the label instance that should be selected.
*/
void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel);
/**
* @brief Sets the segmentation that will be used /monitored by the widget.
*
* @param segmentation A pointer to the segmentation to set.
* @remark You cannot set the segmentation directly if a segmentation node is
* also set. Reset the node (nullptr) if you want to change to direct segmentation
* setting.
* @pre Segmentation node is nullptr.
*/
void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation);
/**
* @brief Sets the segmentation node that will be used /monitored by the widget.
*
* @param node A pointer to the segmentation node.
* @remark If not set some features of the manager are not active
*/
void SetMultiLabelNode(mitk::DataNode* node);
void SetDataStorage(mitk::DataStorage *storage);
void UpdateControls();
virtual void setEnabled(bool enabled);
QStringList &GetLabelStringList();
void SetDefaultLabelNaming(bool defaultLabelNaming);
private Q_SLOTS:
// LabelSet dependent
void OnRenameLabelShortcutActivated();
// reaction to "returnPressed" signal from ...
void OnSearchLabel();
// reaction to the change of labels. If multiple labels are selected, it is ignored.
void OnSelectedLabelChanged(const LabelValueVectorType& labels);
- // LabelSetImage Dependet
+ // LabelSetImage Dependent
void OnCreateDetailedSurface(bool);
void OnCreateSmoothedSurface(bool);
// reaction to the signal "createMask" from QmitkLabelSetTableWidget
void OnCreateMask(bool);
// reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget
void OnCreateCroppedMask(bool);
void OnSavePreset();
void OnLoadPreset();
void OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& position) const;
void OnLabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
void OnModelUpdated();
void OnSegmentationChanged();
private:
enum TableColumns
{
NAME_COL = 0,
LOCKED_COL,
COLOR_COL,
VISIBLE_COL
};
void WaitCursorOn();
void WaitCursorOff();
void RestoreOverrideCursor();
void OnThreadedCalculationDone();
void AddSegmentationObserver();
void RemoveSegmentationObserver();
void OnLabelEvent(mitk::LabelSetImage::LabelValueType labelValue);
void OnGroupEvent(mitk::LabelSetImage::GroupIndexType groupIndex);
Ui::QmitkMultiLabelManagerControls* m_Controls;
QCompleter *m_Completer;
QStringList m_OrganColors;
QStringList m_LabelStringList;
bool m_ProcessingManualSelection;
mitk::DataStorage* m_DataStorage;
mitk::ITKEventObserverGuard m_LabelAddedObserver;
mitk::ITKEventObserverGuard m_LabelModifiedObserver;
mitk::ITKEventObserverGuard m_LabelRemovedObserver;
mitk::ITKEventObserverGuard m_GroupAddedObserver;
mitk::ITKEventObserverGuard m_GroupModifiedObserver;
mitk::ITKEventObserverGuard m_GroupRemovedObserver;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
index 8f5fb670dc..6e468f4857 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
@@ -1,1060 +1,1061 @@
/*============================================================================
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 "QmitkMultiLabelTreeModel.h"
#include <mitkMultiLabelEvents.h>
#include <mitkRenderingManager.h>
#include <mitkLabelSetImageHelper.h>
#include <QmitkStyleManager.h>
class QmitkMultiLabelSegTreeItem
{
public:
enum class ItemType
{
Group,
Label,
Instance
};
QmitkMultiLabelSegTreeItem()
{
};
explicit QmitkMultiLabelSegTreeItem(ItemType type, QmitkMultiLabelSegTreeItem* parentItem,
mitk::Label* label = nullptr, std::string className = ""): m_parentItem(parentItem), m_ItemType(type), m_Label(label), m_ClassName(className)
{
};
~QmitkMultiLabelSegTreeItem()
{
for (auto item : m_childItems)
{
delete item;
}
};
void AppendChild(QmitkMultiLabelSegTreeItem* child)
{
m_childItems.push_back(child);
};
void RemoveChild(std::size_t row)
{
if (row < m_childItems.size())
{
delete m_childItems[row];
m_childItems.erase(m_childItems.begin() + row);
}
};
int Row() const
{
if (m_parentItem)
{
auto finding = std::find(m_parentItem->m_childItems.begin(), m_parentItem->m_childItems.end(), this);
if (finding != m_parentItem->m_childItems.end())
{
return std::distance(m_parentItem->m_childItems.begin(), finding);
}
}
return 0;
};
QmitkMultiLabelSegTreeItem* ParentItem()
{
return m_parentItem;
};
const QmitkMultiLabelSegTreeItem* ParentItem() const
{
return m_parentItem;
};
const QmitkMultiLabelSegTreeItem* NextSibblingItem() const
{
if (m_parentItem)
{
const std::vector<QmitkMultiLabelSegTreeItem*>::size_type row = this->Row();
if (row + 1 < m_parentItem->m_childItems.size())
return m_parentItem->m_childItems[row+1];
}
return nullptr;
};
const QmitkMultiLabelSegTreeItem* PrevSibblingItem() const
{
if (m_parentItem)
{
const std::vector<QmitkMultiLabelSegTreeItem*>::size_type row = this->Row();
if (row > 0)
return m_parentItem->m_childItems[row-1];
}
return nullptr;
};
const QmitkMultiLabelSegTreeItem* RootItem() const
{
auto item = this;
while (item->m_parentItem != nullptr)
{
item = item->m_parentItem;
}
return item;
};
std::size_t GetGroupID() const
{
auto root = this->RootItem();
auto item = this;
if (root == this) return 0;
while (root != item->m_parentItem)
{
item = item->m_parentItem;
}
auto iter = std::find(root->m_childItems.begin(), root->m_childItems.end(), item);
if (root->m_childItems.end() == iter) mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Root does not have an currentItem as child that has root as parent.";
return std::distance(root->m_childItems.begin(), iter);
}
bool HandleAsInstance() const
{
return (ItemType::Instance == m_ItemType) || ((ItemType::Label == m_ItemType) && (m_childItems.size() == 1));
}
mitk::Label* GetLabel() const
{
if (ItemType::Instance == m_ItemType)
{
return m_Label;
}
if (ItemType::Label == m_ItemType)
{
if (m_childItems.empty()) mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Internal label currentItem has no instance currentItem.";
return m_childItems[0]->GetLabel();
}
return nullptr;
};
mitk::LabelSetImage::LabelValueType GetLabelValue() const
{
auto label = this->GetLabel();
if (nullptr == label)
{
mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Called GetLabelValue on an group currentItem.";
}
return label->GetValue();
};
/** returns a vector containing all label values of referenced by this item or its child items.*/
std::vector< mitk::LabelSetImage::LabelValueType> GetLabelsInSubTree() const
{
if (this->m_ItemType == ItemType::Instance)
{
return { this->GetLabelValue() };
}
std::vector< mitk::LabelSetImage::LabelValueType> result;
for (const auto child : this->m_childItems)
{
auto childresult = child->GetLabelsInSubTree();
result.reserve(result.size() + childresult.size());
result.insert(result.end(), childresult.begin(), childresult.end());
}
return result;
}
std::vector<QmitkMultiLabelSegTreeItem*> m_childItems;
QmitkMultiLabelSegTreeItem* m_parentItem = nullptr;
ItemType m_ItemType = ItemType::Group;
mitk::Label::Pointer m_Label;
std::string m_ClassName;
};
QModelIndex GetIndexByItem(const QmitkMultiLabelSegTreeItem* start, const QmitkMultiLabelTreeModel* model)
{
QModelIndex parentIndex = QModelIndex();
if (nullptr != start->m_parentItem)
{
parentIndex = GetIndexByItem(start->m_parentItem, model);
}
else
{
return parentIndex;
}
return model->index(start->Row(), 0, parentIndex);
}
QmitkMultiLabelSegTreeItem* GetGroupItem(QmitkMultiLabelTreeModel::GroupIndexType groupIndex, QmitkMultiLabelSegTreeItem* root)
{
if (nullptr != root && groupIndex < root->m_childItems.size())
{
return root->m_childItems[groupIndex];
}
return nullptr;
}
QmitkMultiLabelSegTreeItem* GetInstanceItem(QmitkMultiLabelTreeModel::LabelValueType labelValue, QmitkMultiLabelSegTreeItem* root)
{
QmitkMultiLabelSegTreeItem* result = nullptr;
for (auto item : root->m_childItems)
{
result = GetInstanceItem(labelValue, item);
if (nullptr != result) return result;
}
if (root->m_ItemType == QmitkMultiLabelSegTreeItem::ItemType::Instance && root->GetLabelValue() == labelValue)
{
return root;
}
return nullptr;
}
const QmitkMultiLabelSegTreeItem* GetFirstInstanceLikeItem(const QmitkMultiLabelSegTreeItem* startItem)
{
const QmitkMultiLabelSegTreeItem* result = nullptr;
if (nullptr != startItem)
{
if (startItem->HandleAsInstance())
{
result = startItem;
}
else if (!startItem->m_childItems.empty())
{
result = GetFirstInstanceLikeItem(startItem->m_childItems.front());
}
}
return result;
}
QmitkMultiLabelSegTreeItem* GetLabelItemInGroup(const std::string& labelName, QmitkMultiLabelSegTreeItem* group)
{
if (nullptr != group)
{
auto predicate = [labelName](const QmitkMultiLabelSegTreeItem* item) { return labelName == item->m_ClassName; };
auto finding = std::find_if(group->m_childItems.begin(), group->m_childItems.end(), predicate);
if (group->m_childItems.end() != finding)
{
return *finding;
}
}
return nullptr;
}
QmitkMultiLabelTreeModel::QmitkMultiLabelTreeModel(QObject *parent) : QAbstractItemModel(parent)
{
m_RootItem = std::make_unique<QmitkMultiLabelSegTreeItem>();
}
QmitkMultiLabelTreeModel ::~QmitkMultiLabelTreeModel()
{
this->SetSegmentation(nullptr);
};
int QmitkMultiLabelTreeModel::columnCount(const QModelIndex& /*parent*/) const
{
return 4;
}
int QmitkMultiLabelTreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;
if (m_Segmentation.IsNull())
return 0;
QmitkMultiLabelSegTreeItem* parentItem = m_RootItem.get();
if (parent.isValid())
parentItem = static_cast<QmitkMultiLabelSegTreeItem *>(parent.internalPointer());
if (parentItem->HandleAsInstance())
{
return 0;
}
return parentItem->m_childItems.size();
}
QVariant QmitkMultiLabelTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
if (!item)
return QVariant();
if (role == Qt::DisplayRole||role == Qt::EditRole)
{
if (TableColumns::NAME_COL == index.column())
{
switch (item->m_ItemType)
{
case QmitkMultiLabelSegTreeItem::ItemType::Group:
{
const auto groupID = item->GetGroupID();
if (m_Segmentation->ExistGroup(groupID))
{
return QVariant(QString::fromStdString(mitk::LabelSetImageHelper::CreateDisplayGroupName(m_Segmentation, groupID)));
}
return QVariant(QString("unknown group"));
}
case QmitkMultiLabelSegTreeItem::ItemType::Label:
{
auto label = item->GetLabel();
if (nullptr == label)
- mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is refering to a label that does not exist.";
+ mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is referring to a label that does not exist.";
QString name = QString::fromStdString(label->GetName());
if (!item->HandleAsInstance())
name = name + QString(" (%1 instances)").arg(item->m_childItems.size());
return QVariant(name);
}
case QmitkMultiLabelSegTreeItem::ItemType::Instance:
{
auto label = item->GetLabel();
if (nullptr == label)
- mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is refering to a label that does not exist.";
+ mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is referring to a label that does not exist.";
return QVariant(QString::fromStdString(label->GetName()) + QString(" [%1]").arg(item->GetLabelValue()));
}
}
}
else
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
if (TableColumns::LOCKED_COL == index.column())
{
return QVariant(label->GetLocked());
}
else if (TableColumns::COLOR_COL == index.column())
{
return QVariant(QColor(label->GetColor().GetRed() * 255, label->GetColor().GetGreen() * 255, label->GetColor().GetBlue() * 255));
}
else if (TableColumns::VISIBLE_COL == index.column())
{
return QVariant(label->GetVisible());
}
}
}
}
else if (role == Qt::ToolTipRole)
{
if (item->m_ItemType == QmitkMultiLabelSegTreeItem::ItemType::Group)
{
return QVariant(QString("Group %1").arg(item->GetGroupID()));
}
else
{
auto label = item->GetLabel();
if (nullptr == label)
mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is referring to a label that does not exist.";
QString name = QString::fromStdString("<b>"+label->GetName()+"</b>");
if (!item->HandleAsInstance())
{
name = QString("Label class: %1\nContaining %2 instances").arg(name).arg(item->m_childItems.size());
}
else
{
name = QString::fromStdString(label->GetName()) + QString("\nLabel instance ID: %1\nPixel value: %2").arg(item->GetLabelValue()).arg(item->GetLabelValue());
}
return QVariant(name);
}
}
else if (role == ItemModelRole::LabelDataRole)
{
auto label = item->GetLabel();
if (nullptr!=label) return QVariant::fromValue<void*>(label);
}
else if (role == ItemModelRole::LabelValueRole)
{
auto label = item->GetLabel();
if (nullptr != label) return QVariant(label->GetValue());
}
else if (role == ItemModelRole::LabelInstanceDataRole)
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
return QVariant::fromValue<void*>(label);
}
}
else if (role == ItemModelRole::LabelInstanceValueRole)
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
return QVariant(label->GetValue());
}
}
else if (role == ItemModelRole::GroupIDRole)
{
QVariant v;
v.setValue(item->GetGroupID());
return v;
}
return QVariant();
}
mitk::Color QtToMitk(const QColor& color)
{
mitk::Color mitkColor;
mitkColor.SetRed(color.red() / 255.0f);
mitkColor.SetGreen(color.green() / 255.0f);
mitkColor.SetBlue(color.blue() / 255.0f);
return mitkColor;
}
bool QmitkMultiLabelTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid())
return false;
auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
if (!item)
return false;
if (role == Qt::EditRole)
{
if (TableColumns::NAME_COL != index.column())
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
if (TableColumns::LOCKED_COL == index.column())
{
label->SetLocked(value.toBool());
}
else if (TableColumns::COLOR_COL == index.column())
{
label->SetColor(QtToMitk(value.value<QColor>()));
}
else if (TableColumns::VISIBLE_COL == index.column())
{
label->SetVisible(value.toBool());
}
m_Segmentation->UpdateLookupTable(label->GetValue());
m_Segmentation->GetLookupTable()->Modified();
m_Segmentation->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else
{
}
return true;
}
}
return false;
}
QModelIndex QmitkMultiLabelTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
auto parentItem = m_RootItem.get();
if (parent.isValid())
parentItem = static_cast<QmitkMultiLabelSegTreeItem *>(parent.internalPointer());
QmitkMultiLabelSegTreeItem *childItem = parentItem->m_childItems[row];
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex QmitkMultiLabelTreeModel::indexOfLabel(mitk::Label::PixelType labelValue) const
{
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return QModelIndex();
auto relevantItem = GetInstanceItem(labelValue, this->m_RootItem.get());
if (nullptr == relevantItem)
return QModelIndex();
auto labelItem = relevantItem->ParentItem();
if (labelItem->m_childItems.size() == 1)
{ //was the only instance of the label, therefor return the label item instat.
relevantItem = labelItem;
}
return GetIndexByItem(relevantItem, this);
}
QModelIndex QmitkMultiLabelTreeModel::indexOfGroup(mitk::LabelSetImage::GroupIndexType groupIndex) const
{
auto relevantItem = GetGroupItem(groupIndex, this->m_RootItem.get());
if (nullptr == relevantItem) QModelIndex();
return GetIndexByItem(relevantItem, this);
}
QModelIndex QmitkMultiLabelTreeModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
QmitkMultiLabelSegTreeItem *childItem = static_cast<QmitkMultiLabelSegTreeItem *>(child.internalPointer());
QmitkMultiLabelSegTreeItem *parentItem = childItem->ParentItem();
if (parentItem == m_RootItem.get())
return QModelIndex();
return createIndex(parentItem->Row(), 0, parentItem);
}
QModelIndex QmitkMultiLabelTreeModel::ClosestLabelInstanceIndex(const QModelIndex& currentIndex) const
{
if (!currentIndex.isValid()) return QModelIndex();
auto currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
if (!currentItem) return QModelIndex();
if (currentItem->RootItem() != this->m_RootItem.get()) mitkThrow() << "Invalid call. Passed currentIndex does not seem to be a valid index of this model. It is either outdated or from another model.";
const QmitkMultiLabelSegTreeItem* resultItem = nullptr;
auto searchItem = currentItem;
const auto rootItem = currentItem->RootItem();
while (searchItem != rootItem)
{
const auto* sibling = searchItem;
while (sibling != nullptr)
{
sibling = sibling->NextSibblingItem();
resultItem = GetFirstInstanceLikeItem(sibling);
if (nullptr != resultItem)
break;
}
if (nullptr != resultItem)
break;
// No next closest label instance on this level -> check for closest before
sibling = searchItem;
while (sibling != nullptr)
{
sibling = sibling->PrevSibblingItem();
resultItem = GetFirstInstanceLikeItem(sibling);
if (nullptr != resultItem)
break;
}
if (nullptr != resultItem)
break;
// No closest label instance before current on this level -> moeve one level up
searchItem = searchItem->ParentItem();
}
if (nullptr == resultItem)
return QModelIndex();
return GetIndexByItem(resultItem, this);
}
QModelIndex QmitkMultiLabelTreeModel::FirstLabelInstanceIndex(const QModelIndex& currentIndex) const
{
const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
if (!currentIndex.isValid())
{
currentItem = this->m_RootItem.get();
}
else
{
currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
}
if (!currentItem) return QModelIndex();
if (currentItem->RootItem() != this->m_RootItem.get()) mitkThrow() << "Invalid call. Passed currentIndex does not seem to be a valid index of this model. It is either outdated or from another model.";
const QmitkMultiLabelSegTreeItem* resultItem = nullptr;
resultItem = GetFirstInstanceLikeItem(currentItem);
if (nullptr == resultItem)
return QModelIndex();
return GetIndexByItem(resultItem, this);
}
///** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
//or instance node). If current index is at the end, an invalid index is returned.*/
//QModelIndex QmitkMultiLabelTreeModel::PrevLabelInstanceIndex(const QModelIndex& currentIndex) const;
std::vector <QmitkMultiLabelTreeModel::LabelValueType> QmitkMultiLabelTreeModel::GetLabelsInSubTree(const QModelIndex& currentIndex) const
{
const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
if (!currentIndex.isValid())
{
currentItem = this->m_RootItem.get();
}
else
{
currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
}
if (!currentItem) return {};
return currentItem->GetLabelsInSubTree();
}
std::vector <QmitkMultiLabelTreeModel::LabelValueType> QmitkMultiLabelTreeModel::GetLabelInstancesOfSameLabelClass(const QModelIndex& currentIndex) const
{
const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
if (currentIndex.isValid())
{
currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
}
if (!currentItem)
return {};
if (QmitkMultiLabelSegTreeItem::ItemType::Group == currentItem->m_ItemType)
return {};
if (QmitkMultiLabelSegTreeItem::ItemType::Instance == currentItem->m_ItemType)
currentItem = currentItem->ParentItem();
return currentItem->GetLabelsInSubTree();
}
Qt::ItemFlags QmitkMultiLabelTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
if (!index.isValid())
return Qt::NoItemFlags;
auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
if (!item)
return Qt::NoItemFlags;
if (TableColumns::NAME_COL != index.column())
{
if (item->HandleAsInstance() &&
((TableColumns::VISIBLE_COL == index.column() && m_AllowVisibilityModification) ||
(TableColumns::COLOR_COL == index.column() && m_AllowVisibilityModification) || //m_AllowVisibilityModification controls visibility and color
(TableColumns::LOCKED_COL == index.column() && m_AllowLockModification)))
{
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
else
{
return Qt::ItemIsEnabled;
}
}
else
{
if (item->HandleAsInstance())
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
else
{
return Qt::ItemIsEnabled;
}
}
return Qt::NoItemFlags;
}
QVariant QmitkMultiLabelTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation))
{
if (TableColumns::NAME_COL == section)
{
return "Name";
}
else if (TableColumns::LOCKED_COL == section)
{
return "Locked";
}
else if (TableColumns::COLOR_COL == section)
{
return "Color";
}
else if (TableColumns::VISIBLE_COL == section)
{
return "Visibility";
}
}
return QVariant();
}
const mitk::LabelSetImage* QmitkMultiLabelTreeModel::GetSegmentation() const
{
return m_Segmentation;
}
void QmitkMultiLabelTreeModel::SetSegmentation(mitk::LabelSetImage* segmentation)
{
if (m_Segmentation != segmentation)
{
this->m_Segmentation = segmentation;
this->AddObserver();
this->UpdateInternalTree();
}
}
/**Helper function that adds a labek into the item tree. Passes back the new created instance iten*/
QmitkMultiLabelSegTreeItem* AddLabelToGroupTree(mitk::Label* label, QmitkMultiLabelSegTreeItem* groupItem, bool& newLabelItemCreated)
{
if (nullptr == groupItem) return nullptr;
if (nullptr == label) return nullptr;
newLabelItemCreated = false;
std::set<std::string> labelNames;
for (auto labelItem : groupItem->m_childItems)
{
labelNames.emplace(labelItem->GetLabel()->GetName());
}
QmitkMultiLabelSegTreeItem* labelItem = nullptr;
auto finding = labelNames.find(label->GetName());
if (finding != labelNames.end())
{ //other label with same name exists
labelItem = groupItem->m_childItems[std::distance(labelNames.begin(), finding)];
}
else
{
newLabelItemCreated = true;
labelItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Label, groupItem, nullptr, label->GetName());
auto predicate = [label](const std::string& name) { return name > label->GetName(); };
auto insertFinding = std::find_if(labelNames.begin(), labelNames.end(), predicate);
groupItem->m_childItems.insert(groupItem->m_childItems.begin() + std::distance(labelNames.begin(), insertFinding), labelItem);
}
auto instanceItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Instance, labelItem, label);
auto predicate = [label](const QmitkMultiLabelSegTreeItem* item) { return item->GetLabelValue() > label->GetValue(); };
auto insertFinding = std::find_if(labelItem->m_childItems.begin(), labelItem->m_childItems.end(), predicate);
labelItem->m_childItems.insert(labelItem->m_childItems.begin() + std::distance(labelItem->m_childItems.begin(), insertFinding), instanceItem);
return instanceItem;
}
void QmitkMultiLabelTreeModel::GenerateInternalGroupTree(unsigned int groupID, QmitkMultiLabelSegTreeItem* groupItem)
{
auto labels = m_Segmentation->GetLabelsByValue(m_Segmentation->GetLabelValuesByGroup(groupID));
for (auto& label : labels)
{
if (label->GetValue()== mitk::LabelSetImage::UNLABELED_VALUE) continue;
bool newItemCreated = false;
AddLabelToGroupTree(label, groupItem, newItemCreated);
}
}
QmitkMultiLabelSegTreeItem* QmitkMultiLabelTreeModel::GenerateInternalTree()
{
auto rootItem = new QmitkMultiLabelSegTreeItem();
if (m_Segmentation.IsNotNull())
{
for (unsigned int groupID = 0; groupID < m_Segmentation->GetNumberOfLayers(); ++groupID)
{
auto groupItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Group, rootItem);
rootItem->AppendChild(groupItem);
GenerateInternalGroupTree(groupID, groupItem);
}
}
return rootItem;
}
void QmitkMultiLabelTreeModel::UpdateInternalTree()
{
emit beginResetModel();
auto newTree = this->GenerateInternalTree();
this->m_RootItem.reset(newTree);
emit endResetModel();
emit modelChanged();
}
void QmitkMultiLabelTreeModel::ITKEventHandler(const itk::EventObject& e)
{
if (mitk::LabelAddedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
this->OnLabelAdded(labelEvent->GetLabelValue());
}
else if (mitk::LabelModifiedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
this->OnLabelModified(labelEvent->GetLabelValue());
}
else if (mitk::LabelRemovedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
this->OnLabelRemoved(labelEvent->GetLabelValue());
}
else if (mitk::GroupAddedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
this->OnGroupAdded(labelEvent->GetGroupID());
}
else if (mitk::GroupModifiedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
this->OnGroupModified(labelEvent->GetGroupID());
}
else if (mitk::GroupRemovedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
this->OnGroupRemoved(labelEvent->GetGroupID());
}
}
void QmitkMultiLabelTreeModel::AddObserver()
{
m_LabelAddedObserver.Reset();
m_LabelModifiedObserver.Reset();
m_LabelRemovedObserver.Reset();
m_GroupAddedObserver.Reset();
m_GroupModifiedObserver.Reset();
m_GroupRemovedObserver.Reset();
if (this->m_Segmentation.IsNotNull())
{
auto& model = *this;
m_LabelAddedObserver.Reset(m_Segmentation, mitk::LabelAddedEvent(), [&model](const itk::EventObject& event){model.ITKEventHandler(event);});
m_LabelModifiedObserver.Reset(m_Segmentation, mitk::LabelModifiedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
m_LabelRemovedObserver.Reset(m_Segmentation, mitk::LabelRemovedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
m_GroupAddedObserver.Reset(m_Segmentation, mitk::GroupAddedEvent(), [&model](const itk::EventObject& event) {
model.ITKEventHandler(event); });
m_GroupModifiedObserver.Reset(m_Segmentation, mitk::GroupModifiedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
m_GroupRemovedObserver.Reset(m_Segmentation, mitk::GroupRemovedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
}
}
void QmitkMultiLabelTreeModel::OnLabelAdded(LabelValueType labelValue)
{
GroupIndexType groupIndex = m_Segmentation->GetGroupIndexOfLabel(labelValue);
auto label = m_Segmentation->GetLabel(labelValue);
if (label.IsNull()) mitkThrow() << "Invalid internal state. Segmentation signaled the addition of a label that does not exist in the segmentation. Invalid label value:" << labelValue;
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return;
auto groupItem = GetGroupItem(groupIndex, this->m_RootItem.get());
bool newLabelCreated = false;
auto instanceItem = AddLabelToGroupTree(label, groupItem, newLabelCreated);
if (newLabelCreated)
{
if (groupItem->m_childItems.size() == 1)
{ //first label added
auto groupIndex = GetIndexByItem(groupItem, this);
emit dataChanged(groupIndex, groupIndex);
this->beginInsertRows(groupIndex, instanceItem->ParentItem()->Row(), instanceItem->ParentItem()->Row());
this->endInsertRows();
}
else
{ //whole new label level added to group item
auto groupIndex = GetIndexByItem(groupItem, this);
this->beginInsertRows(groupIndex, instanceItem->ParentItem()->Row(), instanceItem->ParentItem()->Row());
this->endInsertRows();
}
}
else
{
if (instanceItem->ParentItem()->m_childItems.size() < 3)
{ //second instance item was added, so label item will now able to collapse
// -> the whole label node has to be updated.
auto labelIndex = GetIndexByItem(instanceItem->ParentItem(), this);
emit dataChanged(labelIndex, labelIndex);
this->beginInsertRows(labelIndex, 0, instanceItem->ParentItem()->m_childItems.size()-1);
this->endInsertRows();
}
else
{
// instance item was added to existing label item with multiple instances
//-> just notify the row insertion
auto labelIndex = GetIndexByItem(instanceItem->ParentItem(), this);
this->beginInsertRows(labelIndex, instanceItem->Row(), instanceItem->Row());
this->endInsertRows();
}
}
}
void QmitkMultiLabelTreeModel::OnLabelModified(LabelValueType labelValue)
{
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return;
auto instanceItem = GetInstanceItem(labelValue, this->m_RootItem.get());
if (nullptr == instanceItem)
{
mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel received a LabelModified signal for a label that is not represented in the model. Invalid label: " << labelValue;
+ mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel received a LabelModified signal for a label that is not represented in the model. Invalid label: " << labelValue;
}
auto labelItem = instanceItem->ParentItem();
if (labelItem->m_ClassName == instanceItem->GetLabel()->GetName())
{ //only the state of the label changed, but not its position in the model tree.
auto index = labelItem->HandleAsInstance() ? GetIndexByItem(labelItem, this) : GetIndexByItem(instanceItem, this);
auto rightIndex = index.sibling(index.row(), this->columnCount() - 1);
emit dataChanged(index, rightIndex);
}
else
{ //the name of the label changed and thus its place in the model tree, delete the current item and add a new one
this->OnLabelRemoved(labelValue);
this->OnLabelAdded(labelValue);
}
}
void QmitkMultiLabelTreeModel::OnLabelRemoved(LabelValueType labelValue)
{
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return;
auto instanceItem = GetInstanceItem(labelValue, this->m_RootItem.get());
if (nullptr == instanceItem)
mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel received a LabelRemoved signal for a label that is not represented in the model. Invalid label: " << labelValue;
auto labelItem = instanceItem->ParentItem();
if (labelItem->m_childItems.size() > 2)
{
auto labelIndex = GetIndexByItem(labelItem, this);
this->beginRemoveRows(labelIndex, instanceItem->Row(), instanceItem->Row());
labelItem->RemoveChild(instanceItem->Row());
this->endRemoveRows();
}
else if (labelItem->m_childItems.size() == 2)
{ //After removal only one label is left -> the whole label node is about to be changed (no instances are shown any more).
auto labelIndex = GetIndexByItem(labelItem, this);
this->beginRemoveRows(labelIndex, instanceItem->Row(), instanceItem->Row());
labelItem->RemoveChild(instanceItem->Row());
this->endRemoveRows();
emit dataChanged(labelIndex, labelIndex);
}
else
{ //was the only instance of the label, therefor also remove the label node from the tree.
auto groupItem = labelItem->ParentItem();
auto groupIndex = GetIndexByItem(groupItem, this);
this->beginRemoveRows(groupIndex, labelItem->Row(), labelItem->Row());
groupItem->RemoveChild(labelItem->Row());
this->endRemoveRows();
}
}
void QmitkMultiLabelTreeModel::OnGroupAdded(GroupIndexType groupIndex)
{
if (m_ShowGroups)
{
this->beginInsertRows(QModelIndex(), groupIndex, groupIndex);
auto rootItem = m_RootItem.get();
auto groupItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Group, rootItem);
rootItem->AppendChild(groupItem);
this->GenerateInternalGroupTree(groupIndex, groupItem);
this->endInsertRows();
}
}
void QmitkMultiLabelTreeModel::OnGroupModified(GroupIndexType /*groupIndex*/)
{
//currently not needed
}
void QmitkMultiLabelTreeModel::OnGroupRemoved(GroupIndexType groupIndex)
{
if (m_ShowGroups)
{
this->beginRemoveRows(QModelIndex(), groupIndex, groupIndex);
auto root = m_RootItem.get();
root->RemoveChild(groupIndex);
this->endRemoveRows();
}
}
void QmitkMultiLabelTreeModel::SetAllowVisibilityModification(bool vmod)
{
m_AllowVisibilityModification = vmod;
}
bool QmitkMultiLabelTreeModel::GetAllowVisibilityModification() const
{
return m_AllowVisibilityModification;
}
void QmitkMultiLabelTreeModel::SetAllowLockModification(bool lmod)
{
m_AllowLockModification = lmod;
}
bool QmitkMultiLabelTreeModel::GetAllowLockModification() const
{
return m_AllowLockModification;
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
index a8b4d671d5..33441944ae 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
@@ -1,168 +1,168 @@
/*============================================================================
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 QmitkMultiLabelTreeModel_h
#define QmitkMultiLabelTreeModel_h
#include "mitkLabelSetImage.h"
#include <mitkITKEventObserverGuard.h>
// qt
#include <QAbstractItemModel>
#include "MitkSegmentationUIExports.h"
class QmitkMultiLabelSegTreeItem;
/*!
\class QmitkMultiLabelTreeModel
-The class is used to represent the information of an MITK MultiLabel segmentation instance (labels, spacial groups...).
+The class is used to represent the information of an MITK MultiLabel segmentation instance (labels, spatial groups...).
*/
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
using LabelValueType = mitk::LabelSetImage::LabelValueType;
using GroupIndexType = mitk::LabelSetImage::GroupIndexType;
QmitkMultiLabelTreeModel(QObject *parent = nullptr);
~QmitkMultiLabelTreeModel() override;
void SetSegmentation(mitk::LabelSetImage* segmentation);
const mitk::LabelSetImage* GetSegmentation() const;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
/** returns the index of a passed label value (always first column). If label value does not exist in
segmentation or segmentation is not set an invalid index will be returned.*/
QModelIndex indexOfLabel(mitk::Label::PixelType labelValue) const;
QModelIndex indexOfGroup(mitk::LabelSetImage::GroupIndexType groupIndex) const;
/** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
or instance node). If current index is at the end, an invalid index is returned.*/
QModelIndex ClosestLabelInstanceIndex(const QModelIndex& currentIndex) const;
/** Returns the index to the first child node (or itself) in the tree that behaves like an instance (label node with only one instance
or instance node). If current index is at the end, an invalid index is returned. If an invalid index is passed into the methods,
the search starts at the root; thus the whole tree is search for the first label instance.*/
QModelIndex FirstLabelInstanceIndex(const QModelIndex& currentIndex) const;
///** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
//or instance node). If current index is at the end, an invalid index is returned.*/
//QModelIndex PrevLabelInstanceIndex(const QModelIndex& currentIndex) const;
/** Returns a vector containing all label values of the passed currentIndex or its child items.*/
std::vector <LabelValueType> GetLabelsInSubTree(const QModelIndex& currentIndex) const;
/** Returns a vector containing all label values of all label instances that belong to the same label
* class like the passed index.
*
* If index points to a group or invalid, nothing will be returned.
* @pre currentIndex must be valid and point to a label (class or instance).
*/
std::vector <LabelValueType> GetLabelInstancesOfSameLabelClass(const QModelIndex& currentIndex) const;
enum TableColumns
{
NAME_COL = 0,
LOCKED_COL,
COLOR_COL,
VISIBLE_COL
};
enum ItemModelRole
{
/**This role returns the label object that is associated with an index.
- On group level it always returns an invalid QVariant
- On label level (with multiple instances) it returns the first label instance).
- On instance level it returns the label instance object.*/
LabelDataRole = 64,
/**This role returns only the label value of the label that would be returned by
LabelDataRole.*/
LabelValueRole = 65,
/**Simelar to LabelDataRole, but only returns a valid QVariant if index points only to
a specific instance (so either instance level or label level with only one instance).
You can use that role if you want to assure that only one specific label instance is
referenced by the index.*/
LabelInstanceDataRole = 66,
/**Simelar to LabelValueRole, but like LabelInstanceDataRole only returns a valid QVariant
if index points only to a specific instance (so either instance level or label
level with only one instance).
You can use that role if you want to assure that only one specific label instance is
referenced by the index.*/
LabelInstanceValueRole = 67,
/**This role returns the group ID the item/index belongs to.*/
GroupIDRole = 68
};
bool GetAllowVisibilityModification() const;
bool GetAllowLockModification() const;
public Q_SLOTS:
void SetAllowVisibilityModification(bool vmod);
void SetAllowLockModification(bool lmod);
Q_SIGNALS:
void dataAvailable();
/** Is emitted whenever the model changes are finished (usually a bit later than dataAvailable()).*/
void modelChanged();
protected:
void ITKEventHandler(const itk::EventObject& e);
void OnLabelAdded(LabelValueType labelValue);
void OnLabelModified(LabelValueType labelValue);
void OnLabelRemoved(LabelValueType labelValue);
void OnGroupAdded(GroupIndexType groupIndex);
void OnGroupModified(GroupIndexType groupIndex);
void OnGroupRemoved(GroupIndexType groupIndex);
private:
void AddObserver();
void UpdateInternalTree();
void GenerateInternalGroupTree(unsigned int layerID, QmitkMultiLabelSegTreeItem* layerItem);
QmitkMultiLabelSegTreeItem* GenerateInternalTree();
mitk::LabelSetImage::Pointer m_Segmentation;
std::mutex m_Mutex;
std::unique_ptr<QmitkMultiLabelSegTreeItem> m_RootItem;
bool m_ShowGroups = true;
bool m_ShowVisibility = true;
bool m_ShowLock = true;
bool m_ShowOther = false;
bool m_AllowVisibilityModification = true;
bool m_AllowLockModification = true;
mitk::ITKEventObserverGuard m_LabelAddedObserver;
mitk::ITKEventObserverGuard m_LabelModifiedObserver;
mitk::ITKEventObserverGuard m_LabelRemovedObserver;
mitk::ITKEventObserverGuard m_GroupAddedObserver;
mitk::ITKEventObserverGuard m_GroupModifiedObserver;
mitk::ITKEventObserverGuard m_GroupRemovedObserver;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
index 03b8b4851f..0d1a6864dc 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
@@ -1,186 +1,186 @@
/*============================================================================
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 "QmitkSegWithPreviewToolGUIBase.h"
#include <qcheckbox.h>
#include <qpushbutton.h>
#include <qboxlayout.h>
#include <qlabel.h>
#include <QApplication>
bool DefaultEnableConfirmSegBtnFunction(bool enabled)
{
return enabled;
}
QmitkSegWithPreviewToolGUIBase::QmitkSegWithPreviewToolGUIBase(bool mode2D) : QmitkToolGUI(), m_EnableConfirmSegBtnFnc(DefaultEnableConfirmSegBtnFunction), m_Mode2D(mode2D)
{
connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *)));
}
QmitkSegWithPreviewToolGUIBase::~QmitkSegWithPreviewToolGUIBase()
{
if (m_Tool.IsNotNull())
{
m_Tool->CurrentlyBusy -= mitk::MessageDelegate1<QmitkSegWithPreviewToolGUIBase, bool>(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged);
}
}
void QmitkSegWithPreviewToolGUIBase::OnNewToolAssociated(mitk::Tool *tool)
{
if (m_Tool.IsNotNull())
{
this->DisconnectOldTool(m_Tool);
}
m_Tool = dynamic_cast<mitk::SegWithPreviewTool*>(tool);
if (nullptr == m_MainLayout)
{
// create the visible widgets
m_MainLayout = new QVBoxLayout(this);
m_ConfirmSegBtn = new QPushButton("Confirm Segmentation", this);
connect(m_ConfirmSegBtn, SIGNAL(clicked()), this, SLOT(OnAcceptPreview()));
m_CheckIgnoreLocks = new QCheckBox("Ignore label locks", this);
m_CheckIgnoreLocks->setChecked(m_Tool->GetOverwriteStyle() == mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
m_CheckIgnoreLocks->setToolTip("If checked, the lock state of labels will be ignored when the preview segmentation is confermed. Thus also locked label pixels can be changed by the operation.");
m_CheckMerge = new QCheckBox("Merge with existing content", this);
m_CheckMerge->setChecked(m_Tool->GetMergeStyle()==mitk::MultiLabelSegmentation::MergeStyle::Merge);
- m_CheckMerge->setToolTip("If checked, the preview segmantation will be merged with the existing segmantation into a union. If unchecked, the preview content will replace the old segmantation");
+ m_CheckMerge->setToolTip("If checked, the preview segmentation will be merged with the existing segmentation into a union. If unchecked, the preview content will replace the old segmentation");
m_CheckProcessAll = new QCheckBox("Process all time steps", this);
m_CheckProcessAll->setChecked(false);
m_CheckProcessAll->setToolTip("Process all time steps of the dynamic segmentation and not just the currently visible time step.");
m_CheckProcessAll->setVisible(!m_Mode2D);
- //remark: keept m_CheckProcessAll deactivated in 2D because in this refactoring
+ //remark: keep m_CheckProcessAll deactivated in 2D because in this refactoring
//it should be kept to the status quo and it was not clear how interpolation
//would behave. As soon as it is sorted out we can remove that "feature switch"
//or the comment.
this->InitializeUI(m_MainLayout);
m_MainLayout->addWidget(m_ConfirmSegBtn);
m_MainLayout->addWidget(m_CheckIgnoreLocks);
m_MainLayout->addWidget(m_CheckMerge);
m_MainLayout->addWidget(m_CheckProcessAll);
}
if (m_Tool.IsNotNull())
{
this->ConnectNewTool(m_Tool);
}
}
void QmitkSegWithPreviewToolGUIBase::OnAcceptPreview()
{
if (m_Tool.IsNotNull())
{
if (m_CheckIgnoreLocks->isChecked())
{
m_Tool->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
}
else
{
m_Tool->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
}
if (m_CheckMerge->isChecked())
{
m_Tool->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Merge);
}
else
{
m_Tool->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Replace);
}
m_Tool->SetCreateAllTimeSteps(m_CheckProcessAll->isChecked());
m_ConfirmSegBtn->setEnabled(false);
m_Tool->ConfirmSegmentation();
}
}
void QmitkSegWithPreviewToolGUIBase::DisconnectOldTool(mitk::SegWithPreviewTool* oldTool)
{
oldTool->CurrentlyBusy -= mitk::MessageDelegate1<QmitkSegWithPreviewToolGUIBase, bool>(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged);
}
void QmitkSegWithPreviewToolGUIBase::ConnectNewTool(mitk::SegWithPreviewTool* newTool)
{
newTool->CurrentlyBusy +=
mitk::MessageDelegate1<QmitkSegWithPreviewToolGUIBase, bool>(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged);
m_CheckProcessAll->setVisible(newTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps() > 1);
this->EnableWidgets(true);
}
void QmitkSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* /*mainLayout*/)
{
//default implementation does nothing
}
void QmitkSegWithPreviewToolGUIBase::BusyStateChanged(bool isBusy)
{
if (isBusy)
{
QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
}
else
{
QApplication::restoreOverrideCursor();
}
this->EnableWidgets(!isBusy);
}
void QmitkSegWithPreviewToolGUIBase::EnableWidgets(bool enabled)
{
if (nullptr != m_MainLayout)
{
if (nullptr != m_ConfirmSegBtn)
{
m_ConfirmSegBtn->setEnabled(m_EnableConfirmSegBtnFnc(enabled));
}
if (nullptr != m_CheckIgnoreLocks)
{
m_CheckIgnoreLocks->setEnabled(enabled);
}
if (nullptr != m_CheckMerge)
{
m_CheckMerge->setEnabled(enabled);
}
if (nullptr != m_CheckProcessAll)
{
m_CheckProcessAll->setEnabled(enabled);
}
}
}
void QmitkSegWithPreviewToolGUIBase::SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle)
{
if (nullptr != m_CheckMerge)
{
m_CheckMerge->setChecked(mergeStyle == mitk::MultiLabelSegmentation::MergeStyle::Merge);
}
};
void QmitkSegWithPreviewToolGUIBase::SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle)
{
if (nullptr != m_CheckIgnoreLocks)
{
m_CheckIgnoreLocks->setChecked(overwriteStyle == mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
}
};
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h
index 5ce67a274c..3573f88d1f 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h
@@ -1,95 +1,95 @@
/*============================================================================
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 QmitkSegWithPreviewToolGUIBase_h
#define QmitkSegWithPreviewToolGUIBase_h
#include "QmitkToolGUI.h"
#include "mitkSegWithPreviewTool.h"
#include <MitkSegmentationUIExports.h>
class QCheckBox;
class QPushButton;
class QBoxLayout;
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief GUI base clase for tools derived from mitk::SegWithPreviewTool.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkSegWithPreviewToolGUIBase : public QmitkToolGUI
{
Q_OBJECT
public:
mitkClassMacro(QmitkSegWithPreviewToolGUIBase, QmitkToolGUI);
itkCloneMacro(Self);
itkGetConstMacro(Mode2D, bool);
protected slots:
void OnNewToolAssociated(mitk::Tool *);
void OnAcceptPreview();
protected:
QmitkSegWithPreviewToolGUIBase(bool mode2D);
~QmitkSegWithPreviewToolGUIBase() override;
virtual void DisconnectOldTool(mitk::SegWithPreviewTool* oldTool);
virtual void ConnectNewTool(mitk::SegWithPreviewTool* newTool);
/**This method is called by OnNewToolAssociated if the UI is initialized the
first time to allow derived classes to introduce own UI code. Overwrite to change.
The implementation should ensure that alle widgets needed for the tool UI are
properly allocated. If one needs to eecute time (e.g. to connect events between the tool
and the UI) each time the tool changes, override the functions ConnectNewTool() and
DisconnectOldTool().*/
virtual void InitializeUI(QBoxLayout* mainLayout);
void BusyStateChanged(bool isBusy) override;
using EnableConfirmSegBtnFunctionType = std::function<bool(bool)>;
EnableConfirmSegBtnFunctionType m_EnableConfirmSegBtnFnc;
/**This method is used to control/set the enabled state of the tool UI
widgets. It is e.g. used if the busy state is changed (see BusyStateChanged).
- Override the default implmentation, e.g. if a tool adds his own UI elements
+ Override the default implementation, e.g. if a tool adds his own UI elements
(normally by overriding InitializeUI()) and wants to control how the widgets
are enabled/disabled.*/
virtual void EnableWidgets(bool enabled);
template <class TTool>
TTool* GetConnectedToolAs()
{
return dynamic_cast<TTool*>(m_Tool.GetPointer());
};
void SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle);
void SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle);
private:
QCheckBox* m_CheckIgnoreLocks = nullptr;
QCheckBox* m_CheckMerge = nullptr;
QCheckBox* m_CheckProcessAll = nullptr;
QPushButton* m_ConfirmSegBtn = nullptr;
QBoxLayout* m_MainLayout = nullptr;
/**Indicates if the tool is in 2D or 3D mode.*/
bool m_Mode2D;
mitk::SegWithPreviewTool::Pointer m_Tool;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
index 4881c9cfac..a8be7459c7 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
@@ -1,1497 +1,1497 @@
/*============================================================================
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 "QmitkSlicesInterpolator.h"
#include "QmitkRenderWindow.h"
#include "QmitkRenderWindowWidget.h"
#include "mitkApplyDiffImageOperation.h"
#include "mitkColorProperty.h"
#include "mitkCoreObjectFactory.h"
#include "mitkDiffImageApplier.h"
#include "mitkInteractionConst.h"
#include "mitkLevelWindowProperty.h"
#include "mitkOperationEvent.h"
#include "mitkProgressBar.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include "mitkSegTool2D.h"
#include "mitkSliceNavigationController.h"
#include "mitkSurfaceToImageFilter.h"
#include <mitkTimeNavigationController.h>
#include "mitkToolManager.h"
#include "mitkUndoController.h"
#include <mitkExtractSliceFilter.h>
#include <mitkPlanarCircle.h>
#include <mitkImageReadAccessor.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageWriteAccessor.h>
#include <mitkPlaneProposer.h>
#include <mitkUnstructuredGridClusteringFilter.h>
#include <mitkVtkImageOverwrite.h>
#include <mitkShapeBasedInterpolationAlgorithm.h>
#include <itkCommand.h>
#include <mitkImageToContourFilter.h>
#include <mitkImagePixelReadAccessor.h>
// Includes for the merge operation
#include "mitkImageToContourFilter.h"
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageConverter.h>
#include <QCheckBox>
#include <QCursor>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <vtkDoubleArray.h>
#include <vtkFieldData.h>
#include <vtkPolyVertex.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPolyData.h>
#include <array>
#include <atomic>
#include <thread>
#include <vector>
namespace
{
template <typename T = mitk::BaseData>
itk::SmartPointer<T> GetData(const mitk::DataNode* dataNode)
{
return nullptr != dataNode
? dynamic_cast<T*>(dataNode->GetData())
: nullptr;
}
}
float SURFACE_COLOR_RGB[3] = {0.49f, 1.0f, 0.16f};
const QmitkSlicesInterpolator::ActionToSliceDimensionMapType QmitkSlicesInterpolator::CreateActionToSlicer(const QList<QmitkRenderWindow*>& windows)
{
std::map<QAction *, mitk::SliceNavigationController *> actionToSliceDimension;
for (auto* window : windows)
{
std::string windowName;
auto renderWindowWidget = dynamic_cast<QmitkRenderWindowWidget*>(window->parentWidget());
if (renderWindowWidget)
{
windowName = renderWindowWidget->GetCornerAnnotationText();
}
else
{
windowName = window->GetRenderer()->GetName();
}
auto slicer = window->GetSliceNavigationController();
actionToSliceDimension[new QAction(QString::fromStdString(windowName), nullptr)] = slicer;
}
return actionToSliceDimension;
}
mitk::Image::Pointer ExtractSliceFromImage(mitk::Image* image,
const mitk::PlaneGeometry * contourPlane,
unsigned int timeStep)
{
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// set to false to extract a slice
reslice->SetOverwriteMode(false);
reslice->Modified();
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(image);
extractor->SetTimeStep(timeStep);
extractor->SetWorldGeometry(contourPlane);
extractor->SetVtkOutputRequest(false);
extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
extractor->Update();
mitk::Image::Pointer slice = extractor->GetOutput();
return slice;
}
QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget *parent, const char * /*name*/)
: QWidget(parent),
m_Interpolator(mitk::SegmentationInterpolationController::New()),
m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()),
m_ToolManager(nullptr),
m_Initialized(false),
m_LastSNC(nullptr),
m_LastSliceIndex(0),
m_2DInterpolationEnabled(false),
m_3DInterpolationEnabled(false),
m_CurrentActiveLabelValue(0),
m_FirstRun(true)
{
m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this);
QVBoxLayout *vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode);
m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode);
m_CmbInterpolation->addItem("Disabled");
m_CmbInterpolation->addItem("2-Dimensional");
m_CmbInterpolation->addItem("3-Dimensional");
vboxLayout->addWidget(m_CmbInterpolation);
m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnApply2D);
m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnApplyForAllSlices2D);
m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnApply3D);
m_BtnReinit3DInterpolation = new QPushButton("Reinit Interpolation", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_BtnReinit3DInterpolation);
m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode);
vboxLayout->addWidget(m_ChkShowPositionNodes);
this->HideAllInterpolationControls();
connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int)));
connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked()));
connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked()));
connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked()));
connect(m_BtnReinit3DInterpolation, SIGNAL(clicked()), this, SLOT(OnReinit3DInterpolation()));
connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool)));
connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool)));
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode);
this->setLayout(layout);
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::Pointer command =
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
command->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged);
InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver(itk::ModifiedEvent(), command);
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::Pointer command2 =
itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
command2->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged);
SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver(itk::ModifiedEvent(), command2);
auto command3 = itk::ReceptorMemberCommand<QmitkSlicesInterpolator>::New();
command3->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationAborted);
InterpolationAbortedObserverTag = m_Interpolator->AddObserver(itk::AbortEvent(), command3);
// feedback node and its visualization properties
m_FeedbackNode = mitk::DataNode::New();
mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(m_FeedbackNode);
m_FeedbackNode->SetProperty("binary", mitk::BoolProperty::New(true));
m_FeedbackNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 0.0));
m_FeedbackNode->SetProperty("texture interpolation", mitk::BoolProperty::New(false));
m_FeedbackNode->SetProperty("layer", mitk::IntProperty::New(20));
m_FeedbackNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1)));
m_FeedbackNode->SetProperty("name", mitk::StringProperty::New("Interpolation feedback"));
m_FeedbackNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
m_FeedbackNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_InterpolatedSurfaceNode = mitk::DataNode::New();
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
m_InterpolatedSurfaceNode->SetProperty("name", mitk::StringProperty::New("Surface Interpolation feedback"));
m_InterpolatedSurfaceNode->SetProperty("opacity", mitk::FloatProperty::New(0.5));
m_InterpolatedSurfaceNode->SetProperty("line width", mitk::FloatProperty::New(4.0f));
m_InterpolatedSurfaceNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
m_InterpolatedSurfaceNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_InterpolatedSurfaceNode->SetVisibility(false);
QWidget::setContentsMargins(0, 0, 0, 0);
if (QWidget::layout() != nullptr)
{
QWidget::layout()->setContentsMargins(0, 0, 0, 0);
}
// For running 3D Interpolation in background
// create a QFuture and a QFutureWatcher
connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer()));
connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished()));
connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer()));
m_Timer = new QTimer(this);
connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor()));
}
void QmitkSlicesInterpolator::SetDataStorage(mitk::DataStorage::Pointer storage)
{
if (m_DataStorage == storage)
{
return;
}
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
}
m_DataStorage = storage;
m_SurfaceInterpolator->SetDataStorage(storage);
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
}
}
void QmitkSlicesInterpolator::SetActiveLabelValue(mitk::LabelSetImage::LabelValueType labelValue)
{
bool changedValue = labelValue != this->m_CurrentActiveLabelValue;
this->m_CurrentActiveLabelValue = labelValue;
if (changedValue) this->OnActiveLabelChanged(labelValue);
};
mitk::DataStorage *QmitkSlicesInterpolator::GetDataStorage()
{
if (m_DataStorage.IsNotNull())
{
return m_DataStorage;
}
else
{
return nullptr;
}
}
void QmitkSlicesInterpolator::InitializeWindow(QmitkRenderWindow* window)
{
auto slicer = window->GetSliceNavigationController();
if (slicer == nullptr)
{
MITK_WARN << "Tried setting up interpolation for a render window that does not have a slice navigation controller set";
return;
}
// Has to be initialized
m_LastSNC = slicer;
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer deleteCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
deleteCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted);
m_ControllerToDeleteObserverTag[slicer] = slicer->AddObserver(itk::DeleteEvent(), deleteCommand);
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer sliceChangedCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
sliceChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceChanged);
m_ControllerToSliceObserverTag[slicer] = slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceChangedCommand);
}
void QmitkSlicesInterpolator::Initialize(mitk::ToolManager *toolManager,
const QList<QmitkRenderWindow*>& windows)
{
Q_ASSERT(!windows.empty());
if (m_Initialized)
{
// remove old observers
this->Uninitialize();
}
m_ToolManager = toolManager;
if (m_ToolManager)
{
// set enabled only if a segmentation is selected
mitk::DataNode *node = m_ToolManager->GetWorkingData(0);
QWidget::setEnabled(node != nullptr);
// react whenever the set of selected segmentation changes
m_ToolManager->WorkingDataChanged +=
mitk::MessageDelegate<QmitkSlicesInterpolator>(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified);
m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate<QmitkSlicesInterpolator>(
this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified);
auto* timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController();
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer timeChangedCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
timeChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnTimeChanged);
m_ControllerToTimeObserverTag =
timeNavigationController->AddObserver(mitk::TimeNavigationController::TimeEvent(0), timeChangedCommand);
m_TimePoint = timeNavigationController->GetSelectedTimePoint();
// connect to the slice navigation controller. after each change, call the interpolator
for (auto* window : windows)
{
this->InitializeWindow(window);
}
m_ActionToSlicerMap = CreateActionToSlicer(windows);
}
m_Initialized = true;
}
void QmitkSlicesInterpolator::Uninitialize()
{
if (m_ToolManager.IsNotNull())
{
m_ToolManager->WorkingDataChanged -=
mitk::MessageDelegate<QmitkSlicesInterpolator>(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified);
m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate<QmitkSlicesInterpolator>(
this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified);
}
auto* timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController();
timeNavigationController->RemoveObserver(m_ControllerToTimeObserverTag);
for (auto* slicer : m_ControllerToSliceObserverTag.keys())
{
slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer));
slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer));
}
m_ActionToSlicerMap.clear();
m_ToolManager = nullptr;
m_Initialized = false;
}
QmitkSlicesInterpolator::~QmitkSlicesInterpolator()
{
if (m_Initialized)
{
// remove old observers
this->Uninitialize();
}
WaitForFutures();
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
m_DataStorage->Remove(m_InterpolatedSurfaceNode);
}
// remove observer
m_Interpolator->RemoveObserver(InterpolationAbortedObserverTag);
m_Interpolator->RemoveObserver(InterpolationInfoChangedObserverTag);
m_SurfaceInterpolator->RemoveObserver(SurfaceInterpolationInfoChangedObserverTag);
m_SurfaceInterpolator->SetCurrentInterpolationSession(nullptr);
delete m_Timer;
}
/**
External enableization...
*/
void QmitkSlicesInterpolator::setEnabled(bool enable)
{
QWidget::setEnabled(enable);
// Set the gui elements of the different interpolation modi enabled
if (enable)
{
if (m_2DInterpolationEnabled)
{
this->Show2DInterpolationControls(true);
m_Interpolator->Activate2DInterpolation(true);
}
else if (m_3DInterpolationEnabled)
{
this->Show3DInterpolationControls(true);
this->Show3DInterpolationResult(true);
}
}
// Set all gui elements of the interpolation disabled
else
{
this->HideAllInterpolationControls();
this->Show3DInterpolationResult(false);
}
}
void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status)
{
OnInterpolationActivated(status);
m_Interpolator->Activate2DInterpolation(status);
}
void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status)
{
On3DInterpolationActivated(status);
}
void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status)
{
if (status)
{
OnInterpolationActivated(!status);
On3DInterpolationActivated(!status);
this->Show3DInterpolationResult(false);
}
}
void QmitkSlicesInterpolator::HideAllInterpolationControls()
{
this->Show2DInterpolationControls(false);
this->Show3DInterpolationControls(false);
}
void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show)
{
m_BtnApply2D->setVisible(show);
m_BtnApplyForAllSlices2D->setVisible(show);
}
void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show)
{
m_BtnApply3D->setVisible(show);
m_ChkShowPositionNodes->setVisible(show);
m_BtnReinit3DInterpolation->setVisible(show);
}
void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index)
{
switch (index)
{
case 0: // Disabled
m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation");
this->HideAllInterpolationControls();
this->OnInterpolationActivated(false);
this->On3DInterpolationActivated(false);
this->Show3DInterpolationResult(false);
m_Interpolator->Activate2DInterpolation(false);
break;
case 1: // 2D
m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)");
this->HideAllInterpolationControls();
this->Show2DInterpolationControls(true);
this->OnInterpolationActivated(true);
this->On3DInterpolationActivated(false);
this->Show3DInterpolationResult(false);
m_Interpolator->Activate2DInterpolation(true);
break;
case 2: // 3D
m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)");
this->HideAllInterpolationControls();
this->Show3DInterpolationControls(true);
this->OnInterpolationActivated(false);
this->On3DInterpolationActivated(true);
m_Interpolator->Activate2DInterpolation(false);
break;
default:
MITK_ERROR << "Unknown interpolation method!";
m_CmbInterpolation->setCurrentIndex(0);
break;
}
}
void QmitkSlicesInterpolator::OnShowMarkers(bool state)
{
mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers =
m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)));
for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End();
++it)
{
it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state));
}
}
void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified()
{
if (m_ToolManager->GetWorkingData(0) != nullptr)
{
m_Segmentation = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
m_BtnReinit3DInterpolation->setEnabled(true);
}
else
{
// If no workingdata is set, remove the interpolation feedback
this->GetDataStorage()->Remove(m_FeedbackNode);
m_FeedbackNode->SetData(nullptr);
this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode);
m_InterpolatedSurfaceNode->SetData(nullptr);
m_BtnReinit3DInterpolation->setEnabled(false);
m_CmbInterpolation->setCurrentIndex(0);
return;
}
// Updating the current selected segmentation for the 3D interpolation
this->SetCurrentContourListID();
if (m_2DInterpolationEnabled)
{
OnInterpolationActivated(true); // re-initialize if needed
}
}
void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified()
{
}
void QmitkSlicesInterpolator::OnTimeChanged(itk::Object *sender, const itk::EventObject &e)
{
if (!dynamic_cast<const mitk::TimeNavigationController::TimeEvent*>(&e))
{
return;
}
const auto* timeNavigationController = dynamic_cast<mitk::TimeNavigationController*>(sender);
if (nullptr == timeNavigationController)
{
return;
}
bool timeChanged = m_TimePoint != timeNavigationController->GetSelectedTimePoint();
m_TimePoint = timeNavigationController->GetSelectedTimePoint();
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (timeChanged)
{
if (m_3DInterpolationEnabled)
{
m_InterpolatedSurfaceNode->SetData(nullptr);
}
m_SurfaceInterpolator->Modified();
}
if (nullptr == m_LastSNC)
{
return;
}
if (TranslateAndInterpolateChangedSlice(m_LastSNC->GetCreatedWorldGeometry()))
{
m_LastSNC->GetRenderer()->RequestUpdate();
}
}
void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e)
{
if (!dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent*>(&e))
{
return;
}
auto sliceNavigationController = dynamic_cast<mitk::SliceNavigationController*>(sender);
if (nullptr == sliceNavigationController)
{
return;
}
if(m_2DInterpolationEnabled)
{
this->On2DInterpolationEnabled(m_2DInterpolationEnabled);
}
if (TranslateAndInterpolateChangedSlice(e, sliceNavigationController))
{
sliceNavigationController->GetRenderer()->RequestUpdate();
}
}
bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e,
mitk::SliceNavigationController* sliceNavigationController)
{
const mitk::SliceNavigationController::GeometrySliceEvent* event =
dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent*>(&e);
mitk::TimeGeometry* timeGeometry = event->GetTimeGeometry();
m_LastSNC = sliceNavigationController;
return this->TranslateAndInterpolateChangedSlice(timeGeometry);
}
bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const mitk::TimeGeometry* timeGeometry)
{
if (!m_2DInterpolationEnabled)
{
return false;
}
if (nullptr == timeGeometry)
{
return false;
}
if (!timeGeometry->IsValidTimePoint(m_TimePoint))
{
return false;
}
mitk::SlicedGeometry3D* slicedGeometry =
dynamic_cast<mitk::SlicedGeometry3D*>(timeGeometry->GetGeometryForTimePoint(m_TimePoint).GetPointer());
if (nullptr == slicedGeometry)
{
return false;
}
mitk::PlaneGeometry* plane = dynamic_cast<mitk::PlaneGeometry*>(slicedGeometry->GetPlaneGeometry(m_LastSNC->GetStepper()->GetPos()));
if (nullptr == plane)
{
return false;
}
this->Interpolate(plane);
return true;
}
void QmitkSlicesInterpolator::Interpolate(mitk::PlaneGeometry *plane)
{
if (nullptr == m_ToolManager)
{
return;
}
mitk::DataNode* node = m_ToolManager->GetWorkingData(0);
if (nullptr == node)
{
return;
}
m_Segmentation = dynamic_cast<mitk::Image*>(node->GetData());
if (nullptr == m_Segmentation)
{
return;
}
if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot interpolate WorkingImage. Passed time point is not within the time bounds of WorkingImage. "
"Time point: "
<< m_TimePoint;
return;
}
const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint);
int clickedSliceDimension = -1;
int clickedSliceIndex = -1;
// calculate real slice position, i.e. slice of the image
mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex);
mitk::Image::Pointer interpolation =
m_Interpolator->Interpolate(clickedSliceDimension, clickedSliceIndex, plane, timeStep);
m_FeedbackNode->SetData(interpolation);
// maybe just have a variable that stores the active label color.
if (m_ToolManager)
{
auto* workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode != nullptr)
{
auto* activeLabel = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData())->GetActiveLabel();
if (nullptr != activeLabel)
{
auto activeColor = activeLabel->GetColor();
m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
}
}
}
m_LastSliceIndex = clickedSliceIndex;
}
void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished()
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode && workingNode->GetData())
{
const auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (segmentation == nullptr)
{
MITK_ERROR << "Run3DInterpolation triggered with no MultiLabelSegmentation as working data.";
return;
}
mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(segmentation, m_CurrentActiveLabelValue, segmentation->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint));
if (interpolatedSurface.IsNotNull())
{
m_BtnApply3D->setEnabled(true);;
m_InterpolatedSurfaceNode->SetData(interpolatedSurface);
this->Show3DInterpolationResult(true);
if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode))
{
m_DataStorage->Add(m_InterpolatedSurfaceNode);
}
}
else
{
m_BtnApply3D->setEnabled(false);
if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
{
this->Show3DInterpolationResult(false);
}
}
}
m_BtnReinit3DInterpolation->setEnabled(true);
for (auto* slicer : m_ControllerToSliceObserverTag.keys())
{
slicer->GetRenderer()->RequestUpdate();
}
}
void QmitkSlicesInterpolator::OnAcceptInterpolationClicked()
{
auto* workingNode = m_ToolManager->GetWorkingData(0);
auto* planeGeometry = m_LastSNC->GetCurrentPlaneGeometry();
auto* interpolatedPreview = dynamic_cast<mitk::Image*>(m_FeedbackNode->GetData());
if (nullptr == workingNode || nullptr == interpolatedPreview)
return;
auto* segmentationImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (nullptr == segmentationImage)
return;
if (!segmentationImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the "
"time bounds of segmentation. Time point: "
<< m_TimePoint;
return;
}
const auto timeStep = segmentationImage->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint);
auto interpolatedSlice = mitk::SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, segmentationImage, timeStep)->Clone();
auto activeValue = segmentationImage->GetActiveLabel()->GetValue();
mitk::TransferLabelContentAtTimeStep(
interpolatedPreview,
interpolatedSlice,
segmentationImage->GetConstLabelsByValue(segmentationImage->GetLabelValuesByGroup(segmentationImage->GetActiveLayer())),
timeStep,
0,
mitk::LabelSetImage::UNLABELED_VALUE,
false,
{ {0, mitk::LabelSetImage::UNLABELED_VALUE}, {1, activeValue} }
);
mitk::SegTool2D::WriteBackSegmentationResult(workingNode, planeGeometry, interpolatedSlice, timeStep);
m_FeedbackNode->SetData(nullptr);
}
void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController *slicer)
{
/*
* What exactly is done here:
* 1. We create an empty diff image for the current segmentation
* 2. All interpolated slices are written into the diff image
* 3. Then the diffimage is applied to the original segmentation
*/
if (m_Segmentation)
{
mitk::Image::Pointer segmentation3D = m_Segmentation;
unsigned int timeStep = 0;
if (4 == m_Segmentation->GetDimension())
{
const auto* geometry = m_Segmentation->GetTimeGeometry();
if (!geometry->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept all interpolations. Time point selected by passed SliceNavigationController is not "
"within the time bounds of segmentation. Time point: "
<< m_TimePoint;
return;
}
mitk::Image::Pointer activeLabelImage;
try
{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_Segmentation);
activeLabelImage = mitk::CreateLabelMask(labelSetImage, labelSetImage->GetActiveLabel()->GetValue());
}
catch (const std::exception& e)
{
MITK_ERROR << e.what() << " | NO LABELSETIMAGE IN WORKING NODE\n";
}
m_Interpolator->SetSegmentationVolume(activeLabelImage);
timeStep = geometry->TimePointToTimeStep(m_TimePoint);
auto timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(m_Segmentation);
timeSelector->SetTimeNr(timeStep);
timeSelector->Update();
segmentation3D = timeSelector->GetOutput();
}
// Create an empty diff image for the undo operation
auto diffImage = mitk::Image::New();
diffImage->Initialize(segmentation3D);
// Create scope for ImageWriteAccessor so that the accessor is destroyed right after use
{
mitk::ImageWriteAccessor accessor(diffImage);
// Set all pixels to zero
auto pixelType = mitk::MakeScalarPixelType<mitk::Tool::DefaultSegmentationDataType>();
// For legacy purpose support former pixel type of segmentations (before multilabel)
if (itk::IOComponentEnum::UCHAR == m_Segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType())
pixelType = mitk::MakeScalarPixelType<unsigned char>();
memset(accessor.GetData(), 0, pixelType.GetSize() * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2));
}
// Since we need to shift the plane it must be clone so that the original plane isn't altered
auto slicedGeometry = m_Segmentation->GetSlicedGeometry();
auto planeGeometry = slicer->GetCurrentPlaneGeometry()->Clone();
int sliceDimension = -1;
int sliceIndex = -1;
mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, planeGeometry, sliceDimension, sliceIndex);
const auto numSlices = m_Segmentation->GetDimension(sliceDimension);
mitk::ProgressBar::GetInstance()->AddStepsToDo(numSlices);
std::atomic_uint totalChangedSlices;
// Reuse interpolation algorithm instance for each slice to cache boundary calculations
auto algorithm = mitk::ShapeBasedInterpolationAlgorithm::New();
// Distribute slice interpolations to multiple threads
const auto numThreads = std::min(std::thread::hardware_concurrency(), numSlices);
std::vector<std::vector<unsigned int>> sliceIndices(numThreads);
for (std::remove_const_t<decltype(numSlices)> sliceIndex = 0; sliceIndex < numSlices; ++sliceIndex)
sliceIndices[sliceIndex % numThreads].push_back(sliceIndex);
std::vector<std::thread> threads;
threads.reserve(numThreads);
// This lambda will be executed by the threads
auto interpolate = [=, &interpolator = m_Interpolator, &totalChangedSlices](unsigned int threadIndex)
{
auto clonedPlaneGeometry = planeGeometry->Clone();
auto origin = clonedPlaneGeometry->GetOrigin();
// Go through the sliced indices
for (auto sliceIndex : sliceIndices[threadIndex])
{
slicedGeometry->WorldToIndex(origin, origin);
origin[sliceDimension] = sliceIndex;
slicedGeometry->IndexToWorld(origin, origin);
clonedPlaneGeometry->SetOrigin(origin);
auto interpolation = interpolator->Interpolate(sliceDimension, sliceIndex, clonedPlaneGeometry, timeStep, algorithm);
if (interpolation.IsNotNull())
{
// Setting up the reslicing pipeline which allows us to write the interpolation results back into the image volume
auto reslicer = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// Set overwrite mode to true to write back to the image volume
reslicer->SetInputSlice(interpolation->GetSliceData()->GetVtkImageAccessor(interpolation)->GetVtkImageData());
reslicer->SetOverwriteMode(true);
reslicer->Modified();
auto diffSliceWriter = mitk::ExtractSliceFilter::New(reslicer);
diffSliceWriter->SetInput(diffImage);
diffSliceWriter->SetTimeStep(0);
diffSliceWriter->SetWorldGeometry(clonedPlaneGeometry);
diffSliceWriter->SetVtkOutputRequest(true);
diffSliceWriter->SetResliceTransformByGeometry(diffImage->GetTimeGeometry()->GetGeometryForTimeStep(0));
diffSliceWriter->Modified();
diffSliceWriter->Update();
++totalChangedSlices;
}
mitk::ProgressBar::GetInstance()->Progress();
}
};
m_Interpolator->EnableSliceImageCache();
// Do the interpolation here.
for (size_t threadIndex = 0; threadIndex < numThreads; ++threadIndex)
{
interpolate(threadIndex);
}
m_Interpolator->DisableSliceImageCache();
const mitk::Label::PixelType newDestinationLabel = dynamic_cast<mitk::LabelSetImage *>(m_Segmentation)->GetActiveLabel()->GetValue();
// Do and Undo Operations
if (totalChangedSlices > 0)
{
// Create do/undo operations
auto* doOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep);
auto* undoOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep);
undoOp->SetFactor(-1.0);
auto comment = "Confirm all interpolations (" + std::to_string(totalChangedSlices) + ")";
auto* undoStackItem = new mitk::OperationEvent(mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment);
mitk::OperationEvent::IncCurrGroupEventId();
mitk::OperationEvent::IncCurrObjectEventId();
mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem);
mitk::DiffImageApplier::GetInstanceForUndo()->SetDestinationLabel(newDestinationLabel);
// Apply the changes to the original image
mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation(doOp);
}
m_FeedbackNode->SetData(nullptr);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController *slicer)
{
// this redirect is for calling from outside
if (slicer == nullptr)
OnAcceptAllInterpolationsClicked();
else
AcceptAllInterpolations(slicer);
}
void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked()
{
QMenu orientationPopup(this);
for (auto it = m_ActionToSlicerMap.begin(); it != m_ActionToSlicerMap.end(); ++it)
{
orientationPopup.addAction(it->first);
}
connect(&orientationPopup, SIGNAL(triggered(QAction *)), this, SLOT(OnAcceptAllPopupActivated(QAction *)));
orientationPopup.exec(QCursor::pos());
}
void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked()
{
auto referenceImage = GetData<mitk::Image>(m_ToolManager->GetReferenceData(0));
auto* segmentationDataNode = m_ToolManager->GetWorkingData(0);
auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(segmentationDataNode->GetData());
auto activeLabelColor = labelSetImage->GetActiveLabel()->GetColor();
std::string activeLabelName = labelSetImage->GetActiveLabel()->GetName();
auto segmentation = GetData<mitk::Image>(segmentationDataNode);
if (referenceImage.IsNull() || segmentation.IsNull())
return;
const auto* segmentationGeometry = segmentation->GetTimeGeometry();
if (!referenceImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint) ||
!segmentationGeometry->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept interpolation. Current time point is not within the time bounds of the patient image and segmentation.";
return;
}
auto interpolatedSurface = GetData<mitk::Surface>(m_InterpolatedSurfaceNode);
if (interpolatedSurface.IsNull())
return;
auto surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
surfaceToImageFilter->SetImage(referenceImage);
surfaceToImageFilter->SetMakeOutputBinary(true);
surfaceToImageFilter->SetUShortBinaryPixelType(itk::IOComponentEnum::USHORT == segmentation->GetPixelType().GetComponentType());
surfaceToImageFilter->SetInput(interpolatedSurface);
surfaceToImageFilter->Update();
mitk::Image::Pointer interpolatedSegmentation = surfaceToImageFilter->GetOutput();
auto timeStep = segmentationGeometry->TimePointToTimeStep(m_TimePoint);
const mitk::Label::PixelType newDestinationLabel = labelSetImage->GetActiveLabel()->GetValue();
TransferLabelContentAtTimeStep(
interpolatedSegmentation,
labelSetImage,
labelSetImage->GetConstLabelsByValue(labelSetImage->GetLabelValuesByGroup(labelSetImage->GetActiveLayer())),
timeStep,
0,
0,
false,
{{1, newDestinationLabel}},
mitk::MultiLabelSegmentation::MergeStyle::Merge,
mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
this->Show3DInterpolationResult(false);
std::string name = segmentationDataNode->GetName() + " 3D-interpolation - " + activeLabelName;
mitk::TimeBounds timeBounds;
if (1 < interpolatedSurface->GetTimeSteps())
{
name += "_t" + std::to_string(timeStep);
auto* polyData = vtkPolyData::New();
polyData->DeepCopy(interpolatedSurface->GetVtkPolyData(timeStep));
auto surface = mitk::Surface::New();
surface->SetVtkPolyData(polyData);
interpolatedSurface = surface;
timeBounds = segmentationGeometry->GetTimeBounds(timeStep);
}
else
{
timeBounds = segmentationGeometry->GetTimeBounds(0);
}
auto* surfaceGeometry = static_cast<mitk::ProportionalTimeGeometry*>(interpolatedSurface->GetTimeGeometry());
surfaceGeometry->SetFirstTimePoint(timeBounds[0]);
surfaceGeometry->SetStepDuration(timeBounds[1] - timeBounds[0]);
// Typical file formats for surfaces do not save any time-related information. As a workaround at least for MITK scene files, we have the
- // possibility to seralize this information as properties.
+ // possibility to serialize this information as properties.
interpolatedSurface->SetProperty("ProportionalTimeGeometry.FirstTimePoint", mitk::FloatProperty::New(surfaceGeometry->GetFirstTimePoint()));
interpolatedSurface->SetProperty("ProportionalTimeGeometry.StepDuration", mitk::FloatProperty::New(surfaceGeometry->GetStepDuration()));
auto interpolatedSurfaceDataNode = mitk::DataNode::New();
interpolatedSurfaceDataNode->SetData(interpolatedSurface);
interpolatedSurfaceDataNode->SetName(name);
interpolatedSurfaceDataNode->SetOpacity(0.7f);
interpolatedSurfaceDataNode->SetColor(activeLabelColor);
m_DataStorage->Add(interpolatedSurfaceDataNode, segmentationDataNode);
}
void QmitkSlicesInterpolator::OnReinit3DInterpolation()
{
// Step 1. Load from the isContourPlaneGeometry nodes the contourNodes.
mitk::NodePredicateProperty::Pointer pred =
mitk::NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
mitk::DataStorage::SetOfObjects::ConstPointer contourNodes =
m_DataStorage->GetDerivations(m_ToolManager->GetWorkingData(0), pred);
if (contourNodes->Size() != 0)
{
if (m_ToolManager->GetWorkingData(0) != nullptr)
{
try
{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
if (!labelSetImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
return;
}
mitk::SurfaceInterpolationController::CPIVector newCPIs;
// Adding label and timeStep information for the contourNodes.
for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it)
{
auto contourNode = it->Value();
auto labelID = dynamic_cast<mitk::UShortProperty *>(contourNode->GetProperty("labelID"))->GetValue();
auto timeStep = dynamic_cast<mitk::IntProperty *>(contourNode->GetProperty("timeStep"))->GetValue();
auto planeGeometry = dynamic_cast<mitk::PlanarFigure *>(contourNode->GetData())->GetPlaneGeometry();
auto groupID = labelSetImage->GetGroupIndexOfLabel(labelID);
auto sliceImage = ExtractSliceFromImage(labelSetImage->GetGroupImage(groupID), planeGeometry, timeStep);
mitk::ImageToContourFilter::Pointer contourExtractor = mitk::ImageToContourFilter::New();
contourExtractor->SetInput(sliceImage);
contourExtractor->SetContourValue(labelID);
contourExtractor->Update();
mitk::Surface::Pointer contour = contourExtractor->GetOutput();
if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
continue;
contour->DisconnectPipeline();
newCPIs.emplace_back(contour, planeGeometry->Clone(),labelID,timeStep);
}
m_SurfaceInterpolator->CompleteReinitialization(newCPIs);
}
catch(const std::exception& e)
{
MITK_ERROR << "Exception thrown casting toolmanager working data to labelsetImage";
}
}
}
else
{
m_BtnApply3D->setEnabled(false);
QMessageBox errorInfo;
errorInfo.setWindowTitle("Reinitialize surface interpolation");
errorInfo.setIcon(QMessageBox::Information);
errorInfo.setText("No contours available for the selected segmentation!");
errorInfo.exec();
}
}
void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction *action)
{
try
{
auto iter = m_ActionToSlicerMap.find(action);
if (iter != m_ActionToSlicerMap.end())
{
mitk::SliceNavigationController *slicer = iter->second;
this->AcceptAllInterpolations(slicer);
}
}
catch (...)
{
/* Showing message box with possible memory error */
QMessageBox errorInfo;
errorInfo.setWindowTitle("Interpolation Process");
errorInfo.setIcon(QMessageBox::Critical);
errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!");
errorInfo.exec();
std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl;
}
}
void QmitkSlicesInterpolator::OnInterpolationActivated(bool on)
{
m_2DInterpolationEnabled = on;
try
{
if (m_DataStorage.IsNotNull())
{
if (on && !m_DataStorage->Exists(m_FeedbackNode))
{
m_DataStorage->Add(m_FeedbackNode);
}
}
}
catch (...)
{
// don't care (double add/remove)
}
if (m_ToolManager)
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
mitk::DataNode *referenceNode = m_ToolManager->GetReferenceData(0);
QWidget::setEnabled(workingNode != nullptr);
m_BtnApply2D->setEnabled(on);
m_FeedbackNode->SetVisibility(on);
if (!on)
{
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
if (workingNode)
{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
if (nullptr == labelSetImage)
{
MITK_ERROR << "NO LABELSETIMAGE IN WORKING NODE\n";
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
const auto* activeLabel = labelSetImage->GetActiveLabel();
const auto* segmentation = dynamic_cast<mitk::Image*>(workingNode->GetData());
if (nullptr != activeLabel && nullptr != segmentation)
{
auto activeLabelImage = mitk::CreateLabelMask(labelSetImage, activeLabel->GetValue());
m_Interpolator->SetSegmentationVolume(activeLabelImage);
if (referenceNode)
{
mitk::Image *referenceImage = dynamic_cast<mitk::Image *>(referenceNode->GetData());
m_Interpolator->SetReferenceVolume(referenceImage); // may be nullptr
}
}
}
}
this->UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::Run3DInterpolation()
{
auto workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode == nullptr)
{
MITK_ERROR << "Run3DInterpolation triggered with no working data set.";
return;
}
const auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (segmentation == nullptr)
{
MITK_ERROR << "Run3DInterpolation triggered with no MultiLabelSegmentation as working data.";
return;
}
if (!segmentation->ExistLabel(m_CurrentActiveLabelValue))
{
MITK_ERROR << "Run3DInterpolation triggered with no valid label selected. Currently selected invalid label: "<<m_CurrentActiveLabelValue;
return;
}
m_SurfaceInterpolator->Interpolate(segmentation,m_CurrentActiveLabelValue,segmentation->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint));
}
void QmitkSlicesInterpolator::StartUpdateInterpolationTimer()
{
m_Timer->start(500);
}
void QmitkSlicesInterpolator::StopUpdateInterpolationTimer()
{
if(m_ToolManager)
{
const auto* workingNode = m_ToolManager->GetWorkingData(0);
const auto activeColor = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData())->GetActiveLabel()->GetColor();
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
}
m_Timer->stop();
}
void QmitkSlicesInterpolator::ChangeSurfaceColor()
{
float currentColor[3];
m_InterpolatedSurfaceNode->GetColor(currentColor);
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
m_InterpolatedSurfaceNode->Update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS);
}
void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on)
{
m_3DInterpolationEnabled = on;
try
{
// this->PrepareInputsFor3DInterpolation();
m_SurfaceInterpolator->Modified();
}
catch (...)
{
MITK_ERROR << "Error with 3D surface interpolation!";
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::EnableInterpolation(bool on)
{
// only to be called from the outside world
// just a redirection to OnInterpolationActivated
OnInterpolationActivated(on);
}
void QmitkSlicesInterpolator::Enable3DInterpolation(bool on)
{
// only to be called from the outside world
// just a redirection to OnInterpolationActivated
this->On3DInterpolationActivated(on);
}
void QmitkSlicesInterpolator::UpdateVisibleSuggestion()
{
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject & /*e*/)
{
// something (e.g. undo) changed the interpolation info, we should refresh our display
this->UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::OnInterpolationAborted(const itk::EventObject& /*e*/)
{
m_CmbInterpolation->setCurrentIndex(0);
m_FeedbackNode->SetData(nullptr);
}
void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject & /*e*/)
{
auto workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode == nullptr)
{
MITK_DEBUG << "OnSurfaceInterpolationInfoChanged triggered with no working data set.";
return;
}
const auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (segmentation == nullptr)
{
MITK_DEBUG << "OnSurfaceInterpolationInfoChanged triggered with no MultiLabelSegmentation as working data.";
return;
}
if (!segmentation->ExistLabel(m_CurrentActiveLabelValue))
{
MITK_DEBUG << "OnSurfaceInterpolationInfoChanged triggered with no valid label selected. Currently selected invalid label: " << m_CurrentActiveLabelValue;
return;
}
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (m_3DInterpolationEnabled)
{
m_InterpolatedSurfaceNode->SetData(nullptr);
m_Future = QtConcurrent::run(&QmitkSlicesInterpolator::Run3DInterpolation, this);
m_Watcher.setFuture(m_Future);
}
}
void QmitkSlicesInterpolator::SetCurrentContourListID()
{
// New ContourList = hide current interpolation
Show3DInterpolationResult(false);
if (m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC)
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode)
{
QWidget::setEnabled(true);
if (!workingNode->GetData()->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << m_TimePoint;
return;
}
m_SurfaceInterpolator->SetDistanceImageVolume(50000);
auto segmentationImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage);
}
else
{
QWidget::setEnabled(false);
}
}
}
void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status)
{
if (m_InterpolatedSurfaceNode.IsNotNull())
m_InterpolatedSurfaceNode->SetVisibility(status);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::OnActiveLabelChanged(mitk::Label::PixelType)
{
m_FeedbackNode->SetData(nullptr);
m_InterpolatedSurfaceNode->SetData(nullptr);
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (m_3DInterpolationEnabled)
{
m_SurfaceInterpolator->Modified();
}
if (m_2DInterpolationEnabled)
{
m_FeedbackNode->SetData(nullptr);
this->OnInterpolationActivated(true);
m_LastSNC->SendSlice();
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
this->UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::CheckSupportedImageDimension()
{
if (m_ToolManager->GetWorkingData(0))
{
m_Segmentation = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
if (m_3DInterpolationEnabled && m_Segmentation && ((m_Segmentation->GetDimension() != 3) || (m_Segmentation->GetDimension() != 4)) )
{
QMessageBox info;
info.setWindowTitle("3D Interpolation Process");
info.setIcon(QMessageBox::Information);
info.setText("3D Interpolation is only supported for 3D/4D images at the moment!");
info.exec();
m_CmbInterpolation->setCurrentIndex(0);
}
}
}
void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender,
const itk::EventObject & /*e*/)
{
// Don't know how to avoid const_cast here?!
mitk::SliceNavigationController *slicer =
dynamic_cast<mitk::SliceNavigationController *>(const_cast<itk::Object *>(sender));
if (slicer)
{
m_ControllerToSliceObserverTag.remove(slicer);
m_ControllerToDeleteObserverTag.remove(slicer);
}
}
void QmitkSlicesInterpolator::WaitForFutures()
{
if (m_Watcher.isRunning())
{
m_Watcher.waitForFinished();
}
if (m_PlaneWatcher.isRunning())
{
m_PlaneWatcher.waitForFinished();
}
}
void QmitkSlicesInterpolator::NodeRemoved(const mitk::DataNode* node)
{
if ((m_ToolManager && m_ToolManager->GetWorkingData(0) == node) ||
node == m_FeedbackNode ||
node == m_InterpolatedSurfaceNode)
{
WaitForFutures();
}
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp
index 1e32b96012..bd1b00879f 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp
@@ -1,511 +1,511 @@
/*============================================================================
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 "QmitkTotalSegmentatorToolGUI.h"
#include "mitkProcessExecutor.h"
#include "mitkTotalSegmentatorTool.h"
#include <QApplication>
#include <QDir>
#include <QDirIterator>
#include <QFileDialog>
#include <QIcon>
#include <QmitkStyleManager.h>
#include <QMessageBox>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkTotalSegmentatorToolGUI, "")
QmitkTotalSegmentatorToolGUI::QmitkTotalSegmentatorToolGUI()
: QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc)
{
// Nvidia-smi command returning zero doesn't always imply lack of GPUs.
// Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given.
if (m_GpuLoader.GetGPUCount() == 0)
{
std::string warning = "WARNING: No GPUs were detected on your machine. The TotalSegmentator tool can be very slow.";
this->ShowErrorMessage(warning);
}
m_EnableConfirmSegBtnFnc = [this](bool enabled)
{ return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; };
}
void QmitkTotalSegmentatorToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool)
{
Superclass::ConnectNewTool(newTool);
m_FirstPreviewComputation = true;
}
void QmitkTotalSegmentatorToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
#ifndef _WIN32
m_Controls.sysPythonComboBox->addItem("/usr/bin");
#endif
this->AutoParsePythonPaths();
m_Controls.sysPythonComboBox->addItem("Select");
m_Controls.sysPythonComboBox->setCurrentIndex(0);
m_Controls.pythonEnvComboBox->addItem("Select");
m_Controls.pythonEnvComboBox->setDuplicatesEnabled(false);
m_Controls.pythonEnvComboBox->setDisabled(true);
m_Controls.previewButton->setDisabled(true);
m_Controls.statusLabel->setTextFormat(Qt::RichText);
m_Controls.subtaskComboBox->addItems(VALID_TASKS);
QString welcomeText;
this->SetGPUInfo();
if (m_GpuLoader.GetGPUCount() != 0)
{
welcomeText = "<b>STATUS: </b><i>Welcome to TotalSegmentator tool. You're in luck: " +
QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected.</i>";
}
else
{
welcomeText = "<b>STATUS: </b><i>Welcome to TotalSegmentator tool. Sorry, " +
QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.</i>";
}
connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked()));
connect(m_Controls.clearButton, SIGNAL(clicked()), this, SLOT(OnClearInstall()));
connect(m_Controls.installButton, SIGNAL(clicked()), this, SLOT(OnInstallBtnClicked()));
connect(m_Controls.overrideBox, SIGNAL(stateChanged(int)), this, SLOT(OnOverrideChecked(int)));
connect(m_Controls.pythonEnvComboBox,
QOverload<int>::of(&QComboBox::activated),
[=](int index) { OnPythonPathChanged(m_Controls.pythonEnvComboBox->itemText(index)); });
connect(m_Controls.sysPythonComboBox,
QOverload<int>::of(&QComboBox::activated),
[=](int index) { OnSystemPythonChanged(m_Controls.sysPythonComboBox->itemText(index)); });
QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastCustomPythonPath").toString();
if (!lastSelectedPyEnv.isEmpty() && lastSelectedPyEnv!= "Select")
{
m_Controls.pythonEnvComboBox->insertItem(0, lastSelectedPyEnv);
}
m_Controls.fastBox->setChecked(m_Settings.value("TotalSeg/LastFast").toBool());
const QString storageDir = m_Installer.GetVirtualEnvPath();
m_IsInstalled = this->IsTotalSegmentatorInstalled(storageDir);
if (m_IsInstalled)
{
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(storageDir).first;
m_Installer.SetVirtualEnvPath(m_PythonPath);
this->EnableAll(m_IsInstalled);
welcomeText += " TotalSegmentator is already found installed.";
}
else
{
welcomeText += " TotalSegmentator is not installed. Please click on \"Install TotalSegmentator\" above.";
}
this->WriteStatusMessage(welcomeText);
QIcon deleteIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/edit-delete.svg"));
QIcon arrowIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg"));
m_Controls.clearButton->setIcon(deleteIcon);
m_Controls.previewButton->setIcon(arrowIcon);
mainLayout->addLayout(m_Controls.verticalLayout);
Superclass::InitializeUI(mainLayout);
}
void QmitkTotalSegmentatorToolGUI::SetGPUInfo()
{
std::vector<QmitkGPUSpec> specs = m_GpuLoader.GetAllGPUSpecs();
for (const QmitkGPUSpec &gpuSpec : specs)
{
m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")");
}
if (specs.empty())
{
m_Controls.gpuComboBox->setEditable(true);
m_Controls.gpuComboBox->addItem(QString::number(0));
m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this));
}
}
unsigned int QmitkTotalSegmentatorToolGUI::FetchSelectedGPUFromUI() const
{
QString gpuInfo = m_Controls.gpuComboBox->currentText();
if (m_GpuLoader.GetGPUCount() == 0)
{
return static_cast<unsigned int>(gpuInfo.toInt());
}
else
{
QString gpuId = gpuInfo.split(":", Qt::SkipEmptyParts).first();
return static_cast<unsigned int>(gpuId.toInt());
}
}
void QmitkTotalSegmentatorToolGUI::EnableAll(bool isEnable)
{
m_Controls.previewButton->setEnabled(isEnable);
m_Controls.subtaskComboBox->setEnabled(isEnable);
m_Controls.installButton->setEnabled((!isEnable));
}
void QmitkTotalSegmentatorToolGUI::OnInstallBtnClicked()
{
bool isInstalled = false;
const auto [path, version] = OnSystemPythonChanged(m_Controls.sysPythonComboBox->currentText());
if (path.isEmpty())
{
this->WriteErrorMessage("<b>ERROR: </b>Couldn't find compatible Python.");
return;
}
if (!QmitkSetupVirtualEnvUtil::IsVenvInstalled(path))
{
this->WriteErrorMessage("venv module not found for the selected python to create a new virtual "
"environment. Please install venv or select another compatibile python");
return;
}
// check if python 3.13 and ask for confirmation
if (version.startsWith("3.13") &&
QMessageBox::No == QMessageBox::question(
nullptr,
"Installing TotalSegmentator",
QString("WARNING: This is an unsupported version of Python that may not work. "
"We recommend using a supported Python version between 3.9 and 3.12.\n\n"
"Continue anyway?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No))
{
return;
}
this->WriteStatusMessage("<b>STATUS: </b>Installing TotalSegmentator...");
m_Installer.SetSystemPythonPath(path);
isInstalled = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME);
if (isInstalled)
{
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(m_Installer.GetVirtualEnvPath()).first;
this->WriteStatusMessage("<b>STATUS: </b>Successfully installed TotalSegmentator.");
}
else
{
this->WriteErrorMessage("<b>ERROR: </b>Couldn't install TotalSegmentator.");
}
this->EnableAll(isInstalled);
}
void QmitkTotalSegmentatorToolGUI::OnPreviewBtnClicked()
{
auto tool = this->GetConnectedToolAs<mitk::TotalSegmentatorTool>();
if (nullptr == tool)
{
return;
}
try
{
m_Controls.previewButton->setEnabled(false);
qApp->processEvents();
if (!this->IsTotalSegmentatorInstalled(m_PythonPath))
{
throw std::runtime_error(WARNING_TOTALSEG_NOT_FOUND);
}
bool isFast = m_Controls.fastBox->isChecked();
QString subTask = m_Controls.subtaskComboBox->currentText();
if (subTask != VALID_TASKS[0])
{
isFast = true;
}
tool->SetPythonPath(m_PythonPath.toStdString());
tool->SetGpuId(FetchSelectedGPUFromUI());
tool->SetFast(isFast);
tool->SetSubTask(subTask.toStdString());
this->WriteStatusMessage(QString("<b>STATUS: </b><i>Starting Segmentation task... This might take a while.</i>"));
m_FirstPreviewComputation = false;
tool->UpdatePreview();
m_Controls.previewButton->setEnabled(true);
}
catch (const std::exception &e)
{
std::stringstream errorMsg;
errorMsg << "<b>STATUS: </b>Error while processing parameters for TotalSegmentator segmentation. Reason: "
<< e.what();
this->ShowErrorMessage(errorMsg.str());
this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
m_Controls.previewButton->setEnabled(true);
m_FirstPreviewComputation = true;
return;
}
catch (...)
{
- std::string errorMsg = "Unkown error occured while generation TotalSegmentator segmentation.";
+ std::string errorMsg = "Unknown error occurred while generation TotalSegmentator segmentation.";
this->ShowErrorMessage(errorMsg);
m_Controls.previewButton->setEnabled(true);
m_FirstPreviewComputation = true;
return;
}
this->SetLabelSetPreview(tool->GetPreviewSegmentation());
this->ActualizePreviewLabelVisibility();
this->WriteStatusMessage("<b>STATUS: </b><i>Segmentation task finished successfully.</i>");
QString pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText();
if (!pythonPathTextItem.isEmpty() && pythonPathTextItem != "Select") // only cache if the prediction ended without errors.
{
QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastCustomPythonPath").toString();
if (lastSelectedPyEnv != pythonPathTextItem)
{
m_Settings.setValue("TotalSeg/LastCustomPythonPath", pythonPathTextItem);
}
}
m_Settings.setValue("TotalSeg/LastFast", m_Controls.fastBox->isChecked());
}
void QmitkTotalSegmentatorToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str());
messageBox->exec();
delete messageBox;
MITK_WARN << message;
}
void QmitkTotalSegmentatorToolGUI::WriteStatusMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white");
qApp->processEvents();
}
void QmitkTotalSegmentatorToolGUI::WriteErrorMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red");
qApp->processEvents();
}
bool QmitkTotalSegmentatorToolGUI::IsTotalSegmentatorInstalled(const QString &pythonPath)
{
QString fullPath = pythonPath;
bool isPythonExists = false, isExists = false;
#ifdef _WIN32
isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe"));
if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("Scripts");
isPythonExists =
(!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python.exe")) : isPythonExists;
}
isExists = QFile::exists(fullPath + QDir::separator() + QString("TotalSegmentator.exe")) && isPythonExists;
#else
isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3"));
if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("bin");
isPythonExists =
(!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python3")) : isPythonExists;
}
isExists = QFile::exists(fullPath + QDir::separator() + QString("TotalSegmentator")) && isPythonExists;
#endif
return isExists;
}
void QmitkTotalSegmentatorToolGUI::AutoParsePythonPaths()
{
QString homeDir = QDir::homePath();
std::vector<QString> searchDirs;
#ifdef _WIN32
searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() +
QString("anaconda3"));
#else
// Add search locations for possible standard python paths here
searchDirs.push_back(homeDir + QDir::separator() + "environments");
searchDirs.push_back(homeDir + QDir::separator() + "anaconda3");
searchDirs.push_back(homeDir + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3");
#endif
for (QString searchDir : searchDirs)
{
if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive))
{
if (QDir(searchDir).exists())
{
m_Controls.sysPythonComboBox->addItem("(base): " + searchDir);
searchDir.append((QDir::separator() + QString("envs")));
}
}
for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();)
{
subIt.next();
QString envName = subIt.fileName();
- if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any.
+ if (!envName.startsWith('.')) // Filter out irrelevant hidden folders, if any.
{
m_Controls.pythonEnvComboBox->addItem("(" + envName + "): " + subIt.filePath());
}
}
}
}
std::pair<QString, QString> QmitkTotalSegmentatorToolGUI::OnSystemPythonChanged(const QString &pyEnv)
{
std::pair<QString, QString> pyPath;
if (pyEnv == QString("Select"))
{
m_Controls.previewButton->setDisabled(true);
QString path =
QFileDialog::getExistingDirectory(m_Controls.sysPythonComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnSystemPythonChanged(path); // recall same function for new path validation
bool oldState = m_Controls.sysPythonComboBox->blockSignals(true); // block signal firing while inserting item
m_Controls.sysPythonComboBox->insertItem(0, path);
m_Controls.sysPythonComboBox->setCurrentIndex(0);
m_Controls.sysPythonComboBox->blockSignals(
oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration
}
}
else
{
QString uiPyPath = this->GetPythonPathFromUI(pyEnv);
pyPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath);
}
return pyPath;
}
void QmitkTotalSegmentatorToolGUI::OnPythonPathChanged(const QString &pyEnv)
{
if (pyEnv == QString("Select"))
{
m_Controls.previewButton->setDisabled(true);
QString path =
QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnPythonPathChanged(path); // recall same function for new path validation
bool oldState = m_Controls.pythonEnvComboBox->blockSignals(true); // block signal firing while inserting item
m_Controls.pythonEnvComboBox->insertItem(0, path);
m_Controls.pythonEnvComboBox->setCurrentIndex(0);
m_Controls.pythonEnvComboBox->blockSignals(
oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration
}
}
else if (!this->IsTotalSegmentatorInstalled(this->GetPythonPathFromUI(pyEnv)))
{
this->ShowErrorMessage(WARNING_TOTALSEG_NOT_FOUND);
m_Controls.previewButton->setDisabled(true);
}
else
{// Show positive status meeage
m_Controls.previewButton->setDisabled(false);
QString uiPyPath = this->GetPythonPathFromUI(pyEnv);
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath).first;
}
}
QString QmitkTotalSegmentatorToolGUI::GetPythonPathFromUI(const QString &pyUI) const
{
QString fullPath = pyUI;
if (-1 != fullPath.indexOf(")"))
{
fullPath = fullPath.mid(fullPath.indexOf(")") + 2);
}
return fullPath.simplified();
}
void QmitkTotalSegmentatorToolGUI::OnOverrideChecked(int state)
{
bool isEnabled = false;
if (state == Qt::Checked)
{
isEnabled = true;
m_Controls.previewButton->setDisabled(true);
m_PythonPath.clear();
}
else
{
m_PythonPath.clear();
m_Controls.previewButton->setDisabled(true);
if (m_IsInstalled)
{
const QString pythonPath = m_Installer.GetVirtualEnvPath();
auto pathObject = QmitkSetupVirtualEnvUtil::GetExactPythonPath(pythonPath);
m_PythonPath = pathObject.first;
this->EnableAll(m_IsInstalled);
}
}
m_Controls.pythonEnvComboBox->setEnabled(isEnabled);
}
void QmitkTotalSegmentatorToolGUI::OnClearInstall()
{
QDir folderPath(m_Installer.GetVirtualEnvPath());
if (folderPath.removeRecursively())
{
m_Controls.installButton->setEnabled(true);
m_IsInstalled = false;
if (!m_Controls.overrideBox->isChecked())
{
m_Controls.previewButton->setEnabled(false);
}
}
else
{
MITK_ERROR
<< "The virtual environment couldn't be removed. Please check if you have the required access privileges or, some other process is accessing the folders.";
}
}
bool QmitkTotalSegmentatorToolInstaller::SetupVirtualEnv(const QString& venvName)
{
if (GetSystemPythonPath().isEmpty())
{
return false;
}
if (!QmitkSetupVirtualEnvUtil::IsVenvInstalled(GetSystemPythonPath()))
{
return false;
}
QDir folderPath(GetBaseDir());
folderPath.mkdir(venvName);
if (!folderPath.cd(venvName))
{
return false; // Check if directory creation was successful.
}
mitk::ProcessExecutor::ArgumentListType args;
auto spExec = mitk::ProcessExecutor::New();
auto spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(&PrintProcessEvent);
spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand);
args.push_back("-m");
args.push_back("venv");
args.push_back(venvName.toStdString());
#ifdef _WIN32
QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python.exe";
QString pythonExeFolder = "Scripts";
#else
QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python3";
QString pythonExeFolder = "bin";
#endif
spExec->Execute(GetBaseDir().toStdString(), pythonFile.toStdString(), args); // Setup local virtual environment
if (folderPath.cd(pythonExeFolder))
{
this->SetPythonPath(folderPath.absolutePath());
this->SetPipPath(folderPath.absolutePath());
this->InstallPytorch();
for (auto &package : PACKAGES)
{
this->PipInstall(package.toStdString(), &PrintProcessEvent);
}
std::string pythonCode; // python syntax to check if torch is installed with CUDA.
pythonCode.append("import torch;");
pythonCode.append("print('Pytorch was installed with CUDA') if torch.cuda.is_available() else print('PyTorch was "
"installed WITHOUT CUDA');");
this->ExecutePython(pythonCode, &PrintProcessEvent);
return true;
}
return false;
}
QString QmitkTotalSegmentatorToolInstaller::GetVirtualEnvPath()
{
return STORAGE_DIR + VENV_NAME;
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h
index de8e2df0bc..10ae565d51 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h
@@ -1,182 +1,182 @@
/*============================================================================
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 QmitkTotalSegmentatorToolGUI_h_Included
#define QmitkTotalSegmentatorToolGUI_h_Included
#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h"
#include "QmitkSetupVirtualEnvUtil.h"
#include "QmitknnUNetGPU.h"
#include "ui_QmitkTotalSegmentatorGUIControls.h"
#include <MitkSegmentationUIExports.h>
#include <QMessageBox>
#include <QSettings>
#include <QStandardPaths>
#include <QDir>
/**
* @brief Installer class for TotalSegmentator Tool.
* Class specifies the virtual environment name, install version, packages required to pip install
* and implements SetupVirtualEnv method.
*
*/
class QmitkTotalSegmentatorToolInstaller : public QmitkSetupVirtualEnvUtil
{
public:
const QString VENV_NAME = ".totalsegmentator_v2";
const QString TOTALSEGMENTATOR_VERSION = "2.0.5";
const std::vector<QString> PACKAGES = {QString("Totalsegmentator==") + TOTALSEGMENTATOR_VERSION,
QString("setuptools")}; /* just in case */
const QString STORAGE_DIR;
inline QmitkTotalSegmentatorToolInstaller(
const QString baseDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator()
+ qApp->organizationName() + QDir::separator())
: QmitkSetupVirtualEnvUtil(baseDir), STORAGE_DIR(baseDir){};
bool SetupVirtualEnv(const QString &) override;
QString GetVirtualEnvPath() override;
};
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief GUI for mitk::TotalSegmentatorTool.
\sa mitk::
*/
class MITKSEGMENTATIONUI_EXPORT QmitkTotalSegmentatorToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitkTotalSegmentatorToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
protected slots:
/**
* @brief Qt Slot
*/
void OnPreviewBtnClicked();
/**
* @brief Qt Slot
*/
void OnPythonPathChanged(const QString &);
/**
* @brief Qt Slot
*/
std::pair<QString, QString> OnSystemPythonChanged(const QString &);
/**
* @brief Qt Slot
*/
void OnInstallBtnClicked();
/**
* @brief Qt Slot
*/
void OnOverrideChecked(int);
/**
* @brief Qt Slot
*/
void OnClearInstall();
protected:
QmitkTotalSegmentatorToolGUI();
~QmitkTotalSegmentatorToolGUI() = default;
void ConnectNewTool(mitk::SegWithPreviewTool *newTool) override;
void InitializeUI(QBoxLayout *mainLayout) override;
/**
* @brief Enable (or Disable) GUI elements.
*/
void EnableAll(bool);
/**
- * @brief Searches and parses paths of python virtual enviroments
+ * @brief Searches and parses paths of python virtual environments
* from predefined lookout locations
*/
void AutoParsePythonPaths();
/**
* @brief Checks if TotalSegmentator command is valid in the selected python virtual environment.
*
* @return bool
*/
bool IsTotalSegmentatorInstalled(const QString &);
/**
* @brief Creates a QMessage object and shows on screen.
*/
void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical);
/**
* @brief Writes any message in white on the tool pane.
*/
void WriteStatusMessage(const QString &);
/**
* @brief Writes any message in red on the tool pane.
*/
void WriteErrorMessage(const QString &);
/**
* @brief Adds GPU information to the gpu combo box.
- * In case, there aren't any GPUs avaialble, the combo box will be
+ * In case, there aren't any GPUs available, the combo box will be
* rendered editable.
*/
void SetGPUInfo();
/**
* @brief Returns GPU id of the selected GPU from the Combo box.
*
* @return unsigned int
*/
unsigned int FetchSelectedGPUFromUI() const;
/**
* @brief Get the virtual env path from UI combobox removing any
* extra special characters.
*
* @return QString
*/
QString GetPythonPathFromUI(const QString &) const;
/**
* @brief For storing values like Python path across sessions.
*/
QSettings m_Settings;
QString m_PythonPath;
QmitkGPULoader m_GpuLoader;
Ui_QmitkTotalSegmentatorToolGUIControls m_Controls;
bool m_FirstPreviewComputation = true;
bool m_IsInstalled = false;
EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc;
const std::string WARNING_TOTALSEG_NOT_FOUND =
"TotalSegmentator is not detected in the selected python environment.Please select a valid "
"python environment or install TotalSegmentator.";
const QStringList VALID_TASKS = {
"total",
"cerebral_bleed",
"hip_implant",
"coronary_arteries",
"body",
"lung_vessels",
"pleural_pericard_effusion"
};
QmitkTotalSegmentatorToolInstaller m_Installer;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
index 0cf1cc66bb..e071c03009 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
@@ -1,258 +1,258 @@
/*============================================================================
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 QmitknnUNetFolderParser_h
#define QmitknnUNetFolderParser_h
#include <QDirIterator>
#include <memory>
#include <functional>
#include <QString>
#include <vector>
/**
* @brief Struct to store each (Folder) Node of the hierarchy tree structure.
*
*/
struct FolderNode
{
QString name;
QString path; // parent
std::vector<std::shared_ptr<FolderNode>> subFolders;
};
/**
- * @brief Class to store and retreive folder hierarchy information
+ * @brief Class to store and retrieve folder hierarchy information
* of RESULTS_FOLDER. Only Root node is explicitly stored in m_RootNode.
- * No. of sub levels in the hierachry is defined in the LEVEL constant.
+ * No. of sub levels in the hierarchry is defined in the LEVEL constant.
*
*/
class QmitknnUNetFolderParser
{
public:
/**
* @brief Construct a new QmitknnUNetFolderParser object
* Initializes root folder node object pointer calls
* @param parentFolder
*/
QmitknnUNetFolderParser(const QString parentFolder);
/**
* @brief Destroy the QmitknnUNetFolderParser object
*
*/
~QmitknnUNetFolderParser() = default;
/**
* @brief Returns the "Results Folder" string which is parent path of the root node.
*
* @return QString
*/
QString getResultsFolder();
/**
* @brief Returns the Model Names from root node. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getModelNames()
{
auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
return models;
}
/**
* @brief Returns the task names for a given model. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param modelName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getTasksForModel(const QString &modelName)
{
std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
auto tasks = GetSubFolderNamesFromNode<T>(modelNode);
return tasks;
}
/**
* @brief Returns the models names for a given task. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param taskName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getModelsForTask(const QString &taskName)
{
T modelsForTask;
auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
foreach (QString model, models)
{
QStringList taskList = getTasksForModel<QStringList>(model);
if (taskList.contains(taskName, Qt::CaseInsensitive))
{
modelsForTask << model;
}
}
return modelsForTask;
}
/**
* @brief Returns all the task names present in the root node with possible duplicates.
* Template function, type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getAllTasks()
{
T allTasks;
auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
foreach (QString model, models)
{
allTasks << getTasksForModel<QStringList>(model);
}
return allTasks;
}
/**
* @brief Returns the trainer / planner names for a given task & model. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param taskName
* @param modelName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getTrainerPlannersForTask(const QString &taskName, const QString &modelName)
{
std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
std::shared_ptr<FolderNode> taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode);
auto tps = GetSubFolderNamesFromNode<T>(taskNode);
return tps;
}
/**
* @brief Returns the Folds names for a given trainer,planner,task & model name. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param trainer
* @param planner
* @param taskName
* @param modelName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getFoldsForTrainerPlanner(const QString &trainer,
const QString &planner,
const QString &taskName,
const QString &modelName)
{
std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
std::shared_ptr<FolderNode> taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode);
QString trainerPlanner = trainer + QString("__") + planner;
std::shared_ptr<FolderNode> tpNode = GetSubNodeMatchingNameCrietria(trainerPlanner, taskNode);
auto folds = GetSubFolderNamesFromNode<T>(tpNode);
return folds;
}
private:
const int m_LEVEL = 4;
std::shared_ptr<FolderNode> m_RootNode;
/**
* @brief Returns rule function wrapper to check for specific files at given Result_Folder hierarchy level.
*
* @param level
* @return std::function<bool(QString)>
*/
std::function<bool(QString)> RuleEngine(int level);
/**
* @brief Iterates through the root node and returns the sub FolderNode object Matching Name Crietria
*
* @param queryName
* @param parentNode
* @return std::shared_ptr<FolderNode>
*/
std::shared_ptr<FolderNode> GetSubNodeMatchingNameCrietria(const QString &queryName, std::shared_ptr<FolderNode> parentNode);
/**
* @brief Returns the sub folder names for a folder node object. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param std::shared_ptr<FolderNode>
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T GetSubFolderNamesFromNode(const std::shared_ptr<FolderNode> parent)
{
T folders;
std::vector<std::shared_ptr<FolderNode>> subNodes = parent->subFolders;
for (std::shared_ptr<FolderNode> folder : subNodes)
{
folders.push_back(folder->name);
}
return folders;
}
/**
* @brief Iterates through the sub folder hierarchy upto a level provided
* and create a tree structure.
*
* @param parent
* @param level
*/
void InitDirs(std::shared_ptr<FolderNode> parent, int level);
/**
* @brief Iterates through the sub folder hierarchy upto a level provided
* and clears the sub folder std::vector from each node.
*
* @param parent
* @param level
*/
void DeleteDirs(std::shared_ptr<FolderNode> parent, int level);
/**
* @brief Template function to fetch all folders inside a given path.
* The type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param path
* @return T
*/
template <typename T>
T FetchFoldersFromDir(const QString &path, std::function<bool(QString)> callback)
{
T folders;
for (QDirIterator it(path, QDir::AllDirs, QDirIterator::NoIteratorFlags); it.hasNext();)
{
it.next();
if (!it.fileName().startsWith('.') && callback(it.filePath()))
{
folders.push_back(it.fileName());
}
}
return folders;
}
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp
index d69d23064b..794e1de862 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp
@@ -1,1200 +1,1200 @@
/*============================================================================
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 "QmitknnUNetToolGUI.h"
#include "mitkProcessExecutor.h"
#include "mitknnUnetTool.h"
#include <mitkTimeNavigationController.h>
#include <QApplication>
#include <QDir>
#include <QDirIterator>
#include <QIcon>
#include <QmitkStyleManager.h>
#include <QmitknnUNetEnsembleLayout.h>
#include <QtGlobal>
#include <algorithm>
#include <ctkCollapsibleGroupBox.h>
#include <itksys/SystemTools.hxx>
#include <nlohmann/json.hpp>
#include <mitkIOUtil.h>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitknnUNetToolGUI, "")
QmitknnUNetToolGUI::QmitknnUNetToolGUI() : QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc)
{
// Nvidia-smi command returning zero doesn't always imply lack of GPUs.
// Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given.
if (m_GpuLoader.GetGPUCount() == 0)
{
std::string warning = "WARNING: No GPUs were detected on your machine. The nnUNet tool might not work.";
this->ShowErrorMessage(warning);
}
// define predicates for multi modal data selection combobox
auto imageType = mitk::TNodePredicateDataType<mitk::Image>::New();
auto labelSetImageType = mitk::NodePredicateNot::New(mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
m_MultiModalPredicate = mitk::NodePredicateAnd::New(imageType, labelSetImageType).GetPointer();
m_nnUNetThread = new QThread(this);
m_Worker = new nnUNetDownloadWorker;
m_Worker->moveToThread(m_nnUNetThread);
m_EnableConfirmSegBtnFnc = [this](bool enabled)
{
return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false;
};
}
QmitknnUNetToolGUI::~QmitknnUNetToolGUI()
{
m_nnUNetThread->quit();
m_nnUNetThread->wait();
}
void QmitknnUNetToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool)
{
Superclass::ConnectNewTool(newTool);
newTool->IsTimePointChangeAwareOff();
m_FirstPreviewComputation = true;
}
void QmitknnUNetToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
#ifndef _WIN32
m_Controls.pythonEnvComboBox->addItem("/usr/bin");
#endif
m_Controls.pythonEnvComboBox->addItem("Select");
AutoParsePythonPaths();
SetGPUInfo();
connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewRequested()));
connect(m_Controls.modeldirectoryBox,
SIGNAL(directoryChanged(const QString &)),
this,
SLOT(OnDirectoryChanged(const QString &)));
connect(
m_Controls.modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &)));
connect(m_Controls.taskBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTaskChanged(const QString &)));
connect(
m_Controls.plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &)));
connect(m_Controls.multiModalBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int)));
connect(m_Controls.pythonEnvComboBox,
#if QT_VERSION >= 0x050F00 // 5.15
SIGNAL(textActivated(const QString &)),
#elif QT_VERSION >= 0x050C00 // 5.12
SIGNAL(currentTextChanged(const QString &)),
#endif
this,
SLOT(OnPythonPathChanged(const QString &)));
connect(m_Controls.refreshdirectoryBox, SIGNAL(clicked()), this, SLOT(OnRefreshPresssed()));
connect(m_Controls.clearCacheButton, SIGNAL(clicked()), this, SLOT(OnClearCachePressed()));
connect(m_Controls.startDownloadButton, SIGNAL(clicked()), this, SLOT(OnDownloadModel()));
connect(m_Controls.stopDownloadButton, SIGNAL(clicked()), this, SLOT(OnStopDownload()));
// Qthreads
qRegisterMetaType<mitk::ProcessExecutor::Pointer>();
qRegisterMetaType<mitk::ProcessExecutor::ArgumentListType>();
connect(this, &QmitknnUNetToolGUI::Operate, m_Worker, &nnUNetDownloadWorker::DoWork);
connect(m_Worker, &nnUNetDownloadWorker::Exit, this, &QmitknnUNetToolGUI::OnDownloadWorkerExit);
connect(m_nnUNetThread, &QThread::finished, m_Worker, &QObject::deleteLater);
m_Controls.multiModalValueLabel->setStyleSheet("font-weight: bold; color: white");
m_Controls.multiModalValueLabel->setVisible(false);
m_Controls.requiredModalitiesLabel->setVisible(false);
m_Controls.stopDownloadButton->setVisible(false);
m_Controls.previewButton->setEnabled(false);
QIcon refreshIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg"));
m_Controls.refreshdirectoryBox->setIcon(refreshIcon);
QIcon dirIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg"));
m_Controls.modeldirectoryBox->setIcon(dirIcon);
m_Controls.refreshdirectoryBox->setEnabled(true);
QIcon stopIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/status/dialog-error.svg"));
m_Controls.stopDownloadButton->setIcon(stopIcon);
m_Controls.statusLabel->setTextFormat(Qt::RichText);
if (m_GpuLoader.GetGPUCount() != 0)
{
WriteStatusMessage(QString("<b>STATUS: </b><i>Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) +
" GPUs were detected.</i>"));
}
else
{
WriteErrorMessage(QString("<b>STATUS: </b><i>Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) +
" GPUs were detected.</i>"));
}
mainLayout->addLayout(m_Controls.verticalLayout);
Superclass::InitializeUI(mainLayout);
m_UI_ROWS = m_Controls.advancedSettingsLayout->rowCount(); // Must do. Row count is correct only here.
this->DisableEverything();
QString lastSelectedPyEnv = m_Settings.value("nnUNet/LastPythonPath").toString();
m_Controls.pythonEnvComboBox->setCurrentText(lastSelectedPyEnv);
}
void QmitknnUNetToolGUI::EnableWidgets(bool enabled)
{
Superclass::EnableWidgets(enabled);
}
void QmitknnUNetToolGUI::ClearAllModalities()
{
m_Controls.multiModalBox->setChecked(false);
this->ClearAllModalLabels();
}
void QmitknnUNetToolGUI::ClearAllModalLabels()
{
for (auto modalLabel : m_ModalLabels)
{
delete modalLabel; // delete the layout item
m_ModalLabels.pop_back();
}
m_Controls.advancedSettingsLayout->update();
}
void QmitknnUNetToolGUI::DisableEverything()
{
m_Controls.modeldirectoryBox->setEnabled(false);
m_Controls.refreshdirectoryBox->setEnabled(false);
m_Controls.previewButton->setEnabled(false);
m_Controls.multiModalValueLabel->setVisible(false);
m_Controls.multiModalBox->setEnabled(false);
this->ClearAllComboBoxes();
this->ClearAllModalities();
}
void QmitknnUNetToolGUI::ClearAllComboBoxes()
{
m_Controls.modelBox->clear();
m_Controls.taskBox->clear();
m_Controls.foldBox->clear();
m_Controls.trainerBox->clear();
m_Controls.plannerBox->clear();
for (auto &layout : m_EnsembleParams)
{
layout->modelBox->clear();
layout->trainerBox->clear();
layout->plannerBox->clear();
layout->foldBox->clear();
}
}
std::vector<mitk::Image::ConstPointer> QmitknnUNetToolGUI::FetchMultiModalImagesFromUI()
{
std::vector<mitk::Image::ConstPointer> modals;
if (m_Controls.multiModalBox->isChecked() && !m_Modalities.empty())
{
std::set<std::string> nodeNames; // set container for keeping names of all nodes to check if they are added twice.
for (QmitkSingleNodeSelectionWidget *modality : m_Modalities)
{
mitk::DataNode::Pointer node = modality->GetSelectedNode();
if (nodeNames.find(node->GetName()) == nodeNames.end())
{
modals.push_back(dynamic_cast<const mitk::Image *>(node->GetData()));
nodeNames.insert(node->GetName());
}
else
{
throw std::runtime_error("Same modality is selected more than once. Please change your selection.");
break;
}
}
}
return modals;
}
bool QmitknnUNetToolGUI::IsNNUNetInstalled(const QString &pythonPath)
{
QString fullPath = pythonPath;
#ifdef _WIN32
if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("Scripts");
}
#else
if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("bin");
}
#endif
fullPath = fullPath.mid(fullPath.indexOf(" ") + 1);
bool isExists = QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")) &&
QFile::exists(fullPath + QDir::separator() + QString("python3"));
return isExists;
}
void QmitknnUNetToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str());
messageBox->exec();
delete messageBox;
MITK_WARN << message;
}
void QmitknnUNetToolGUI::WriteStatusMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white");
}
void QmitknnUNetToolGUI::WriteErrorMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red");
}
void QmitknnUNetToolGUI::ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer tool)
{
if (m_EnsembleParams[0]->modelBox->currentText() == m_EnsembleParams[1]->modelBox->currentText())
{
throw std::runtime_error("Both models you have selected for ensembling are the same.");
}
QString taskName = m_Controls.taskBox->currentText();
bool isPPJson = m_Controls.postProcessingCheckBox->isChecked();
std::vector<mitk::ModelParams> requestQ;
QString ppDirFolderNamePart1 = "ensemble_";
QStringList ppDirFolderNameParts;
for (auto &layout : m_EnsembleParams)
{
QStringList ppDirFolderName;
QString modelName = layout->modelBox->currentText();
ppDirFolderName << modelName;
ppDirFolderName << "__";
QString trainer = layout->trainerBox->currentText();
ppDirFolderName << trainer;
ppDirFolderName << "__";
QString planId = layout->plannerBox->currentText();
ppDirFolderName << planId;
if (!this->IsModelExists(modelName, taskName, QString(trainer + "__" + planId)))
{
std::string errorMsg = "The configuration " + modelName.toStdString() +
" you have selected doesn't exist. Check your Results Folder again.";
throw std::runtime_error(errorMsg);
}
std::vector<std::string> testfold = FetchSelectedFoldsFromUI(layout->foldBox);
mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, testfold);
requestQ.push_back(modelObject);
ppDirFolderNameParts << ppDirFolderName.join(QString(""));
}
tool->EnsembleOn();
if (isPPJson)
{
QString ppJsonFilePossibility1 = QDir::cleanPath(
m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" +
QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.first() + "--" +
ppDirFolderNameParts.last() + QDir::separator() + "postprocessing.json");
QString ppJsonFilePossibility2 = QDir::cleanPath(
m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" +
QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.last() + "--" +
ppDirFolderNameParts.first() + QDir::separator() + "postprocessing.json");
if (QFile(ppJsonFilePossibility1).exists())
{
tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility1.toStdString());
const QString statusMsg = "<i>Post Processing JSON file found: </i>" + ppJsonFilePossibility1;
this->WriteStatusMessage(statusMsg);
}
else if (QFile(ppJsonFilePossibility2).exists())
{
tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility2.toStdString());
const QString statusMsg = "<i>Post Processing JSON file found:</i>" + ppJsonFilePossibility2;
this->WriteStatusMessage(statusMsg);
}
else
{
std::string errorMsg =
"No post processing file was found for the selected ensemble combination. Continuing anyway...";
this->ShowErrorMessage(errorMsg);
}
}
tool->m_ParamQ.clear();
tool->m_ParamQ = requestQ;
}
void QmitknnUNetToolGUI::ProcessModelParams(mitk::nnUNetTool::Pointer tool)
{
tool->EnsembleOff();
std::vector<mitk::ModelParams> requestQ;
QString modelName = m_Controls.modelBox->currentText();
QString taskName = m_Controls.taskBox->currentText();
QString trainer = m_Controls.trainerBox->currentText();
QString planId = m_Controls.plannerBox->currentText();
std::vector<std::string> fetchedFolds = this->FetchSelectedFoldsFromUI(m_Controls.foldBox);
mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, fetchedFolds);
requestQ.push_back(modelObject);
tool->m_ParamQ.clear();
tool->m_ParamQ = requestQ;
}
bool QmitknnUNetToolGUI::IsModelExists(const QString &modelName, const QString &taskName, const QString &trainerPlanner)
{
QString modelSearchPath =
QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + modelName +
QDir::separator() + taskName + QDir::separator() + trainerPlanner);
if (QDir(modelSearchPath).exists())
{
return true;
}
return false;
}
void QmitknnUNetToolGUI::CheckAllInCheckableComboBox(ctkCheckableComboBox *foldBox)
{
// Recalling all added items to check-mark it.
const QAbstractItemModel *qaim = foldBox->checkableModel();
auto rows = qaim->rowCount();
for (std::remove_const_t<decltype(rows)> i = 0; i < rows; ++i)
{
const QModelIndex mi = qaim->index(i, 0);
foldBox->setCheckState(mi, Qt::Checked);
}
}
std::pair<QStringList, QStringList> QmitknnUNetToolGUI::ExtractTrainerPlannerFromString(QStringList trainerPlanners)
{
QString splitterString = "__";
QStringList trainers, planners;
for (const auto &trainerPlanner : trainerPlanners)
{
trainers << trainerPlanner.split(splitterString, Qt::SkipEmptyParts).first();
planners << trainerPlanner.split(splitterString, Qt::SkipEmptyParts).last();
}
trainers.removeDuplicates();
planners.removeDuplicates();
return std::make_pair(trainers, planners);
}
std::vector<std::string> QmitknnUNetToolGUI::FetchSelectedFoldsFromUI(ctkCheckableComboBox *foldBox)
{
std::vector<std::string> folds;
if (foldBox->noneChecked())
{
this->CheckAllInCheckableComboBox(foldBox);
}
QModelIndexList foldList = foldBox->checkedIndexes();
for (const auto &index : foldList)
{
QString foldQString = foldBox->itemText(index.row());
if(foldQString != "dummy_element_that_nobody_can_see")
{
foldQString = foldQString.split("_", Qt::SkipEmptyParts).last();
folds.push_back(foldQString.toStdString());
}
else
{
throw std::runtime_error("Folds are not recognized. Please check if your nnUNet results folder structure is legitimate");
}
}
return folds;
}
void QmitknnUNetToolGUI::UpdateCacheCountOnUI()
{
QString cacheText = m_CACHE_COUNT_BASE_LABEL + QString::number(m_Cache.size());
m_Controls.cacheCountLabel->setText(cacheText);
}
void QmitknnUNetToolGUI::AddToCache(size_t &hashKey, mitk::LabelSetImage::ConstPointer mlPreview)
{
nnUNetCache *newCacheObj = new nnUNetCache;
newCacheObj->m_SegCache = mlPreview;
m_Cache.insert(hashKey, newCacheObj);
MITK_INFO << "New hash: " << hashKey << " " << newCacheObj->m_SegCache.GetPointer();
this->UpdateCacheCountOnUI();
}
void QmitknnUNetToolGUI::SetGPUInfo()
{
std::vector<QmitkGPUSpec> specs = m_GpuLoader.GetAllGPUSpecs();
for (const QmitkGPUSpec &gpuSpec : specs)
{
m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")");
}
if (specs.empty())
{
m_Controls.gpuComboBox->setEditable(true);
m_Controls.gpuComboBox->addItem(QString::number(0));
m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this));
}
}
unsigned int QmitknnUNetToolGUI::FetchSelectedGPUFromUI()
{
QString gpuInfo = m_Controls.gpuComboBox->currentText();
if (m_GpuLoader.GetGPUCount() == 0)
{
return static_cast<unsigned int>(gpuInfo.toInt());
}
else
{
QString gpuId = gpuInfo.split(":", Qt::SkipEmptyParts).first();
return static_cast<unsigned int>(gpuId.toInt());
}
}
QString QmitknnUNetToolGUI::FetchResultsFolderFromEnv()
{
const char *pathVal = itksys::SystemTools::GetEnv("RESULTS_FOLDER");
QString retVal;
if (pathVal)
{
retVal = QString::fromUtf8(pathVal);
}
else
{
retVal = m_Settings.value("nnUNet/LastRESULTS_FOLDERPath").toString();
}
return retVal;
}
void QmitknnUNetToolGUI::DumpJSONfromPickle(const QString &picklePath)
{
const QString pickleFile = picklePath + QDir::separator() + m_PICKLE_FILENAME;
const QString jsonFile = picklePath + QDir::separator() + m_MITK_EXPORT_JSON_FILENAME;
if (!QFile::exists(jsonFile))
{
mitk::ProcessExecutor::Pointer spExec = mitk::ProcessExecutor::New();
mitk::ProcessExecutor::ArgumentListType args;
args.push_back("-c");
std::string pythonCode; // python syntax to parse plans.pkl file and export as Json file.
pythonCode.append("import pickle;");
pythonCode.append("import json;");
pythonCode.append("loaded_pickle = pickle.load(open('");
pythonCode.append(pickleFile.toStdString());
pythonCode.append("','rb'));");
pythonCode.append("modal_dict = {key: loaded_pickle[key] for key in loaded_pickle.keys() if key in "
"['modalities','num_modalities']};");
pythonCode.append("json.dump(modal_dict, open('");
pythonCode.append(jsonFile.toStdString());
pythonCode.append("', 'w'))");
args.push_back(pythonCode);
try
{
spExec->Execute(m_PythonPath.toStdString(), "python3", args);
}
catch (const mitk::Exception &e)
{
MITK_ERROR << "Pickle parsing FAILED!" << e.GetDescription();
this->WriteStatusMessage(
"Parsing failed in backend. Multiple Modalities will now have to be manually entered by the user.");
}
}
}
void QmitknnUNetToolGUI::ExportAvailableModelsAsJSON(const QString &resultsFolder)
{
const QString jsonPath = resultsFolder + QDir::separator() + m_AVAILABLE_MODELS_JSON_FILENAME;
if (!QFile::exists(jsonPath))
{
auto spExec = mitk::ProcessExecutor::New();
mitk::ProcessExecutor::ArgumentListType args;
args.push_back("--export");
args.push_back(resultsFolder.toStdString());
try
{
spExec->Execute(m_PythonPath.toStdString(), "nnUNet_print_available_pretrained_models", args);
}
catch (const mitk::Exception &e)
{
MITK_ERROR << "Exporting information FAILED." << e.GetDescription();
this->WriteStatusMessage("Exporting information FAILED.");
}
}
}
void QmitknnUNetToolGUI::DisplayMultiModalInfoFromJSON(const QString &jsonPath)
{
std::ifstream file(jsonPath.toStdString());
if (file.is_open())
{
auto jsonObj = nlohmann::json::parse(file, nullptr, false);
if (jsonObj.is_discarded() || !jsonObj.is_object())
{
MITK_ERROR << "Could not parse \"" << jsonPath.toStdString() << "\" as JSON object!";
return;
}
auto num_mods = jsonObj["num_modalities"].get<int>();
this->ClearAllModalLabels();
if (num_mods > 1)
{
m_Controls.multiModalBox->setChecked(true);
m_Controls.multiModalBox->setEnabled(false);
m_Controls.multiModalValueLabel->setText(QString::number(num_mods));
OnModalitiesNumberChanged(num_mods);
m_Controls.advancedSettingsLayout->update();
auto obj = jsonObj["modalities"];
int count = 0;
for (const auto &value : obj)
{
QLabel *label = new QLabel(QString::fromStdString("<i>" + value.get<std::string>() + "</i>"), this);
m_ModalLabels.push_back(label);
m_Controls.advancedSettingsLayout->addWidget(label, m_UI_ROWS + 1 + count, 0);
count++;
}
m_Controls.advancedSettingsLayout->update();
}
else
{
m_Controls.multiModalBox->setChecked(false);
}
}
}
void QmitknnUNetToolGUI::FillAvailableModelsInfoFromJSON(const QString &jsonPath)
{
std::ifstream file(jsonPath.toStdString());
if (file.is_open() && m_Controls.availableBox->count() < 1)
{
auto jsonObj = nlohmann::json::parse(file, nullptr, false);
if (jsonObj.is_discarded() || !jsonObj.is_object())
{
MITK_ERROR << "Could not parse \"" << jsonPath.toStdString() << "\" as JSON object!";
return;
}
for (const auto &obj : jsonObj.items())
{
m_Controls.availableBox->addItem(QString::fromStdString(obj.key()));
}
}
}
mitk::ModelParams QmitknnUNetToolGUI::MapToRequest(const QString &modelName,
const QString &taskName,
const QString &trainer,
const QString &planId,
const std::vector<std::string> &folds)
{
mitk::ModelParams requestObject;
requestObject.model = modelName.toStdString();
requestObject.trainer = trainer.toStdString();
requestObject.planId = planId.toStdString();
requestObject.task = taskName.toStdString();
requestObject.folds = folds;
mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs<mitk::nnUNetTool>();
requestObject.inputName = tool->GetRefNode()->GetName();
requestObject.timeStamp =
std::to_string(mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint());
return requestObject;
}
void QmitknnUNetToolGUI::SetComboBoxToNone(ctkCheckableComboBox* comboBox)
{
comboBox->clear();
comboBox->addItem("dummy_element_that_nobody_can_see");
qobject_cast<QListView *>(comboBox->view())->setRowHidden(0, true); // For the cosmetic purpose of showing "None" on the combobox.
}
/* ---------------------SLOTS---------------------------------------*/
void QmitknnUNetToolGUI::OnPreviewRequested()
{
mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs<mitk::nnUNetTool>();
if (nullptr != tool)
{
QString pythonPathTextItem = "";
try
{
size_t hashKey(0);
m_Controls.previewButton->setEnabled(false); // To prevent misclicked back2back prediction.
qApp->processEvents();
tool->PredictOn(); // purposefully placed to make tool->GetMTime different than before.
QString modelName = m_Controls.modelBox->currentText();
if (modelName.startsWith("ensemble", Qt::CaseInsensitive))
{
this->ProcessEnsembleModelsParams(tool);
}
else
{
this->ProcessModelParams(tool);
}
pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText();
QString pythonPath = m_PythonPath;
if (!this->IsNNUNetInstalled(pythonPath))
{
throw std::runtime_error("nnUNet is not detected in the selected python environment. Please select a valid "
"python environment or install nnUNet.");
}
tool->SetPythonPath(pythonPath.toStdString());
tool->SetModelDirectory(m_ParentFolder->getResultsFolder().toStdString());
// checkboxes
tool->SetMirror(m_Controls.mirrorBox->isChecked());
tool->SetMixedPrecision(m_Controls.mixedPrecisionBox->isChecked());
tool->SetNoPip(false);
bool doCache = m_Controls.enableCachingCheckBox->isChecked();
// Spinboxes
tool->SetGpuId(FetchSelectedGPUFromUI());
// Multi-Modal
tool->MultiModalOff();
if (m_Controls.multiModalBox->isChecked())
{
tool->m_OtherModalPaths.clear();
tool->m_OtherModalPaths = FetchMultiModalImagesFromUI();
tool->MultiModalOn();
}
if (doCache)
{
hashKey = nnUNetCache::GetUniqueHash(tool->m_ParamQ);
if (m_Cache.contains(hashKey))
{
tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before.
}
}
if (tool->GetPredict())
{
tool->m_InputBuffer = nullptr;
this->WriteStatusMessage(
QString("<b>STATUS: </b><i>Starting Segmentation task... This might take a while.</i>"));
m_FirstPreviewComputation = false;
tool->UpdatePreview();
if (nullptr == tool->GetOutputBuffer())
{
this->SegmentationProcessFailed();
}
else
{
this->SegmentationResultHandler(tool);
if (doCache)
{
this->AddToCache(hashKey, tool->GetOutputBuffer());
}
tool->ClearOutputBuffer();
}
tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before.
}
else
{
MITK_INFO << "won't do segmentation. Key found: " << QString::number(hashKey).toStdString();
if (m_Cache.contains(hashKey))
{
nnUNetCache *cacheObject = m_Cache[hashKey];
MITK_INFO << "fetched pointer " << cacheObject->m_SegCache.GetPointer();
tool->SetOutputBuffer(const_cast<mitk::LabelSetImage *>(cacheObject->m_SegCache.GetPointer()));
this->SegmentationResultHandler(tool, true);
}
}
m_Controls.previewButton->setEnabled(true);
}
catch (const std::exception &e)
{
std::stringstream errorMsg;
errorMsg << "<b>STATUS: </b>Error while processing parameters for nnUNet segmentation. Reason: " << e.what();
this->ShowErrorMessage(errorMsg.str());
this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
m_Controls.previewButton->setEnabled(true);
tool->PredictOff();
m_FirstPreviewComputation = true;
return;
}
catch (...)
{
- std::string errorMsg = "Unkown error occured while generation nnUNet segmentation.";
+ std::string errorMsg = "Unknown error occurred while generation nnUNet segmentation.";
this->ShowErrorMessage(errorMsg);
m_Controls.previewButton->setEnabled(true);
tool->PredictOff();
m_FirstPreviewComputation = true;
return;
}
if (!pythonPathTextItem.isEmpty())
{ // only cache if the prediction ended without errors.
m_Settings.setValue("nnUNet/LastPythonPath", pythonPathTextItem);
}
}
}
void QmitknnUNetToolGUI::OnRefreshPresssed()
{
const QString resultsFolder = m_Controls.modeldirectoryBox->directory();
this->OnDirectoryChanged(resultsFolder);
}
void QmitknnUNetToolGUI::OnDirectoryChanged(const QString &resultsFolder)
{
m_IsResultsFolderValid = false;
m_Controls.previewButton->setEnabled(false);
this->ClearAllComboBoxes();
this->ClearAllModalities();
m_ParentFolder = std::make_shared<QmitknnUNetFolderParser>(resultsFolder);
auto tasks = m_ParentFolder->getAllTasks<QStringList>();
tasks.removeDuplicates();
std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); });
m_Settings.setValue("nnUNet/LastRESULTS_FOLDERPath", resultsFolder);
}
void QmitknnUNetToolGUI::OnModelChanged(const QString &model)
{
if (model.isEmpty())
{
return;
}
this->ClearAllModalities();
auto selectedTask = m_Controls.taskBox->currentText();
ctkComboBox *box = qobject_cast<ctkComboBox *>(sender());
if (box == m_Controls.modelBox)
{
if (model == m_VALID_MODELS.last())
{
m_Controls.trainerBox->setVisible(false);
m_Controls.trainerLabel->setVisible(false);
m_Controls.plannerBox->setVisible(false);
m_Controls.plannerLabel->setVisible(false);
m_Controls.foldBox->setVisible(false);
m_Controls.foldLabel->setVisible(false);
m_Controls.previewButton->setEnabled(false);
this->ShowEnsembleLayout(true);
auto models = m_ParentFolder->getModelsForTask<QStringList>(m_Controls.taskBox->currentText());
models.removeDuplicates();
models.removeOne(m_VALID_MODELS.last());
for (auto &layout : m_EnsembleParams)
{
layout->modelBox->clear();
layout->trainerBox->clear();
layout->plannerBox->clear();
std::for_each(models.begin(),
models.end(),
[&layout, this](QString model)
{
if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive))
layout->modelBox->addItem(model);
});
}
}
else
{
m_Controls.trainerBox->setVisible(true);
m_Controls.trainerLabel->setVisible(true);
m_Controls.plannerBox->setVisible(true);
m_Controls.plannerLabel->setVisible(true);
m_Controls.foldBox->setVisible(true);
m_Controls.foldLabel->setVisible(true);
m_Controls.previewButton->setEnabled(false);
this->ShowEnsembleLayout(false);
m_Controls.trainerBox->clear();
m_Controls.plannerBox->clear();
auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask<QStringList>(selectedTask, model);
if(trainerPlanners.isEmpty())
{
this->ShowErrorMessage("No plans.pkl found for "+model.toStdString()+". Check your directory or download the task again.");
this->SetComboBoxToNone(m_Controls.foldBox);
return;
}
QStringList trainers, planners;
std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners);
std::for_each(
trainers.begin(), trainers.end(), [this](QString trainer) { m_Controls.trainerBox->addItem(trainer); });
std::for_each(
planners.begin(), planners.end(), [this](QString planner) { m_Controls.plannerBox->addItem(planner); });
}
}
else if (!m_EnsembleParams.empty())
{
m_Controls.previewButton->setEnabled(false);
for (auto &layout : m_EnsembleParams)
{
if (box == layout->modelBox)
{
layout->trainerBox->clear();
layout->plannerBox->clear();
auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask<QStringList>(selectedTask, model);
if(trainerPlanners.isEmpty())
{
this->ShowErrorMessage("No plans.pkl found for "+model.toStdString()+". Check your directory or download the task again.");
this->SetComboBoxToNone(layout->foldBox);
return;
}
QStringList trainers, planners;
std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners);
std::for_each(trainers.begin(),
trainers.end(),
[&layout](const QString &trainer) { layout->trainerBox->addItem(trainer); });
std::for_each(planners.begin(),
planners.end(),
[&layout](const QString &planner) { layout->plannerBox->addItem(planner); });
break;
}
}
}
}
void QmitknnUNetToolGUI::OnTaskChanged(const QString &task)
{
if (task.isEmpty())
{
return;
}
m_Controls.modelBox->clear();
auto models = m_ParentFolder->getModelsForTask<QStringList>(task);
models.removeDuplicates();
if (!models.contains(m_VALID_MODELS.last(), Qt::CaseInsensitive))
{
models << m_VALID_MODELS.last(); // add ensemble even if folder doesn't exist
}
std::for_each(models.begin(),
models.end(),
[this](QString model)
{
if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive))
m_Controls.modelBox->addItem(model);
});
}
void QmitknnUNetToolGUI::OnTrainerChanged(const QString &plannerSelected)
{
if (plannerSelected.isEmpty())
{
return;
}
m_IsResultsFolderValid = false;
QString parentPath;
auto *box = qobject_cast<ctkComboBox *>(sender());
if (box == m_Controls.plannerBox)
{
m_Controls.foldBox->clear();
auto selectedTrainer = m_Controls.trainerBox->currentText();
auto selectedTask = m_Controls.taskBox->currentText();
auto selectedModel = m_Controls.modelBox->currentText();
auto folds = m_ParentFolder->getFoldsForTrainerPlanner<QStringList>(
selectedTrainer, plannerSelected, selectedTask, selectedModel);
if(folds.isEmpty())
{
this->ShowErrorMessage("No valid folds found. Check your directory or download the task again.");
this->SetComboBoxToNone(m_Controls.foldBox);
return;
}
std::for_each(folds.begin(),
folds.end(),
[this](QString fold)
{
if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet
m_Controls.foldBox->addItem(fold);
});
if (m_Controls.foldBox->count() != 0)
{
m_IsResultsFolderValid = true;
this->CheckAllInCheckableComboBox(m_Controls.foldBox);
auto tempPath = QStringList() << m_ParentFolder->getResultsFolder() << "nnUNet" << selectedModel << selectedTask
<< QString("%1__%2").arg(selectedTrainer, plannerSelected);
parentPath = QDir::cleanPath(tempPath.join(QDir::separator()));
}
}
else if (!m_EnsembleParams.empty())
{
for (auto &layout : m_EnsembleParams)
{
if (box == layout->plannerBox)
{
layout->foldBox->clear();
auto selectedTrainer = layout->trainerBox->currentText();
auto selectedTask = m_Controls.taskBox->currentText();
auto selectedModel = layout->modelBox->currentText();
auto folds = m_ParentFolder->getFoldsForTrainerPlanner<QStringList>(
selectedTrainer, plannerSelected, selectedTask, selectedModel);
if(folds.isEmpty())
{
this->ShowErrorMessage("No valid folds found. Check your directory.");
this->SetComboBoxToNone(layout->foldBox);
return;
}
std::for_each(folds.begin(),
folds.end(),
[&layout](const QString &fold)
{
if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet
layout->foldBox->addItem(fold);
});
if (layout->foldBox->count() != 0)
{
this->CheckAllInCheckableComboBox(layout->foldBox);
m_IsResultsFolderValid = true;
auto tempPath = QStringList() << m_ParentFolder->getResultsFolder() << "nnUNet" << selectedModel
<< selectedTask << QString("%1__%2").arg(selectedTrainer, plannerSelected);
parentPath = QDir::cleanPath(tempPath.join(QDir::separator()));
}
break;
}
}
}
if (m_IsResultsFolderValid)
{
m_Controls.previewButton->setEnabled(true);
const QString mitkJsonFile = parentPath + QDir::separator() + m_MITK_EXPORT_JSON_FILENAME;
this->DumpJSONfromPickle(parentPath);
if (QFile::exists(mitkJsonFile))
{
this->DisplayMultiModalInfoFromJSON(mitkJsonFile);
}
}
}
void QmitknnUNetToolGUI::OnPythonPathChanged(const QString &pyEnv)
{
if (pyEnv == QString("Select"))
{
QString path =
QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnPythonPathChanged(path); // recall same function for new path validation
m_Controls.pythonEnvComboBox->insertItem(0, path);
m_Controls.pythonEnvComboBox->setCurrentIndex(0);
}
}
else if (!this->IsNNUNetInstalled(pyEnv))
{
std::string warning =
"WARNING: nnUNet is not detected on the Python environment you selected. Please select another "
"environment or create one. For more info refer https://github.com/MIC-DKFZ/nnUNet";
this->ShowErrorMessage(warning);
this->DisableEverything();
m_Controls.availableBox->clear();
}
else
{
m_Controls.modeldirectoryBox->setEnabled(true);
m_Controls.refreshdirectoryBox->setEnabled(true);
m_Controls.multiModalBox->setEnabled(true);
QString setVal = this->FetchResultsFolderFromEnv();
if (!setVal.isEmpty())
{
m_Controls.modeldirectoryBox->setDirectory(setVal);
}
this->OnRefreshPresssed();
m_PythonPath = pyEnv.mid(pyEnv.indexOf(" ") + 1);
#ifdef _WIN32
if (!(m_PythonPath.endsWith("Scripts", Qt::CaseInsensitive) || m_PythonPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
m_PythonPath += QDir::separator() + QString("Scripts");
}
#else
if (!(m_PythonPath.endsWith("bin", Qt::CaseInsensitive) || m_PythonPath.endsWith("bin/", Qt::CaseInsensitive)))
{
m_PythonPath += QDir::separator() + QString("bin");
}
#endif
// Export available model info as json and fill them for Download
QString tempPath = QString::fromStdString(mitk::IOUtil::GetTempPath());
this->ExportAvailableModelsAsJSON(tempPath);
const QString jsonPath = tempPath + QDir::separator() + m_AVAILABLE_MODELS_JSON_FILENAME;
if (QFile::exists(jsonPath))
{
this->FillAvailableModelsInfoFromJSON(jsonPath);
}
}
}
void QmitknnUNetToolGUI::OnCheckBoxChanged(int state)
{
bool visibility = false;
if (state == Qt::Checked)
{
visibility = true;
}
ctkCheckBox *box = qobject_cast<ctkCheckBox *>(sender());
if (box != nullptr)
{
if (box->objectName() == QString("multiModalBox"))
{
m_Controls.requiredModalitiesLabel->setVisible(visibility);
m_Controls.multiModalValueLabel->setVisible(visibility);
if (!visibility)
{
this->OnModalitiesNumberChanged(0);
m_Controls.multiModalValueLabel->setText("0");
this->ClearAllModalLabels();
}
}
}
}
void QmitknnUNetToolGUI::OnModalitiesNumberChanged(int num)
{
while (num > static_cast<int>(m_Modalities.size()))
{
QmitkSingleNodeSelectionWidget *multiModalBox = new QmitkSingleNodeSelectionWidget(this);
mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs<mitk::nnUNetTool>();
multiModalBox->SetDataStorage(tool->GetDataStorage());
multiModalBox->SetInvalidInfo("Select corresponding modalities");
multiModalBox->SetNodePredicate(m_MultiModalPredicate);
multiModalBox->setObjectName(QString("multiModal_" + QString::number(m_Modalities.size() + 1)));
m_Controls.advancedSettingsLayout->addWidget(multiModalBox, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3);
m_Modalities.push_back(multiModalBox);
}
while (num < static_cast<int>(m_Modalities.size()) && !m_Modalities.empty())
{
QmitkSingleNodeSelectionWidget *child = m_Modalities.back();
delete child; // delete the layout item
m_Modalities.pop_back();
}
m_Controls.advancedSettingsLayout->update();
}
void QmitknnUNetToolGUI::AutoParsePythonPaths()
{
QString homeDir = QDir::homePath();
std::vector<QString> searchDirs;
#ifdef _WIN32
searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() +
QString("anaconda3"));
#else
// Add search locations for possible standard python paths here
searchDirs.push_back(homeDir + QDir::separator() + "environments");
searchDirs.push_back(homeDir + QDir::separator() + "anaconda3");
searchDirs.push_back(homeDir + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3");
#endif
for (QString searchDir : searchDirs)
{
if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive))
{
if (QDir(searchDir).exists())
{
m_Controls.pythonEnvComboBox->insertItem(0, "(base): " + searchDir);
searchDir.append((QDir::separator() + QString("envs")));
}
}
for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();)
{
subIt.next();
QString envName = subIt.fileName();
- if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any.
+ if (!envName.startsWith('.')) // Filter out irrelevant hidden folders, if any.
{
m_Controls.pythonEnvComboBox->insertItem(0, "(" + envName + "): " + subIt.filePath());
}
}
}
m_Controls.pythonEnvComboBox->setCurrentIndex(-1);
}
void QmitknnUNetToolGUI::SegmentationProcessFailed()
{
m_FirstPreviewComputation = true;
this->WriteErrorMessage(
"<b>STATUS: </b><i>Error in the segmentation process. <br>No resulting segmentation can be loaded.</i>");
this->setCursor(Qt::ArrowCursor);
std::stringstream stream;
stream << "Error in the segmentation process. No resulting segmentation can be loaded.";
this->ShowErrorMessage(stream.str());
}
void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool, bool forceRender)
{
if (forceRender)
{
tool->RenderOutputBuffer();
}
this->SetLabelSetPreview(tool->GetPreviewSegmentation());
this->WriteStatusMessage("<b>STATUS: </b><i>Segmentation task finished successfully.</i>");
this->ActualizePreviewLabelVisibility();
}
void QmitknnUNetToolGUI::ShowEnsembleLayout(bool visible)
{
if (m_EnsembleParams.empty())
{
ctkCollapsibleGroupBox *groupBoxModel1 = new ctkCollapsibleGroupBox(this);
auto lay1 = std::make_unique<QmitknnUNetTaskParamsUITemplate>(groupBoxModel1);
groupBoxModel1->setObjectName(QString::fromUtf8("model_1_Box"));
groupBoxModel1->setTitle(QString::fromUtf8("Model 1"));
groupBoxModel1->setMinimumSize(QSize(0, 0));
groupBoxModel1->setCollapsedHeight(5);
groupBoxModel1->setCollapsed(false);
groupBoxModel1->setFlat(true);
groupBoxModel1->setAlignment(Qt::AlignRight);
m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 5, 0, 1, 2);
connect(lay1->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &)));
connect(
lay1->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &)));
m_EnsembleParams.push_back(std::move(lay1));
ctkCollapsibleGroupBox *groupBoxModel2 = new ctkCollapsibleGroupBox(this);
auto lay2 = std::make_unique<QmitknnUNetTaskParamsUITemplate>(groupBoxModel2);
groupBoxModel2->setObjectName(QString::fromUtf8("model_2_Box"));
groupBoxModel2->setTitle(QString::fromUtf8("Model 2"));
groupBoxModel2->setMinimumSize(QSize(0, 0));
groupBoxModel2->setCollapsedHeight(5);
groupBoxModel2->setCollapsed(false);
groupBoxModel2->setFlat(true);
groupBoxModel2->setAlignment(Qt::AlignLeft);
m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 5, 2, 1, 2);
connect(lay2->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &)));
connect(
lay2->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &)));
m_EnsembleParams.push_back(std::move(lay2));
}
for (auto &layout : m_EnsembleParams)
{
layout->setVisible(visible);
}
}
void QmitknnUNetToolGUI::OnDownloadModel()
{
auto selectedTask = m_Controls.availableBox->currentText();
if(!selectedTask.isEmpty())
{
auto spExec = mitk::ProcessExecutor::New();
mitk::ProcessExecutor::ArgumentListType args;
args.push_back(selectedTask.toStdString());
this->WriteStatusMessage(
"Downloading the requested task in to the selected Results Folder. This might take some time "
"depending on your internet connection...");
m_Processes["DOWNLOAD"] = spExec;
if (!m_nnUNetThread->isRunning())
{
MITK_DEBUG << "Starting thread...";
m_nnUNetThread->start();
}
QString resultsFolder = m_ParentFolder->getResultsFolder();
emit Operate(resultsFolder, m_PythonPath, spExec, args);
m_Controls.stopDownloadButton->setVisible(true);
m_Controls.startDownloadButton->setVisible(false);
}
}
void QmitknnUNetToolGUI::OnDownloadWorkerExit(const bool isSuccess, const QString message)
{
if (isSuccess)
{
this->WriteStatusMessage(message + QString(" Click Refresh Results Folder to use the new Task."));
}
else
{
MITK_ERROR << "Download FAILED! " << message.toStdString();
this->WriteStatusMessage(QString("Download failed. Check your internet connection. " + message));
}
m_Controls.stopDownloadButton->setVisible(false);
m_Controls.startDownloadButton->setVisible(true);
}
void QmitknnUNetToolGUI::OnStopDownload()
{
mitk::ProcessExecutor::Pointer spExec = m_Processes["DOWNLOAD"];
spExec->KillProcess();
this->WriteStatusMessage("Download Killed by the user.");
m_Controls.stopDownloadButton->setVisible(false);
m_Controls.startDownloadButton->setVisible(true);
}
void QmitknnUNetToolGUI::OnClearCachePressed()
{
m_Cache.clear();
this->UpdateCacheCountOnUI();
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h
index 0d4f016f35..1af7ce8032 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h
@@ -1,405 +1,405 @@
/*============================================================================
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 QmitknnUNetToolGUI_h
#define QmitknnUNetToolGUI_h
#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h"
#include "QmitknnUNetFolderParser.h"
#include "QmitknnUNetGPU.h"
#include "QmitknnUNetWorker.h"
#include "mitkProcessExecutor.h"
#include "mitknnUnetTool.h"
#include "ui_QmitknnUNetToolGUIControls.h"
#include <MitkSegmentationUIExports.h>
#include <QCache>
#include <QMessageBox>
#include <QSettings>
#include <QThread>
#include <QmitkSingleNodeSelectionWidget.h>
#include <QmitknnUNetEnsembleLayout.h>
#include <boost/functional/hash.hpp>
#include <unordered_map>
class nnUNetCache
{
public:
mitk::LabelSetImage::ConstPointer m_SegCache;
static size_t GetUniqueHash(std::vector<mitk::ModelParams> &requestQ)
{
size_t hashCode = 0;
for (mitk::ModelParams &request : requestQ)
{
boost::hash_combine(hashCode, request.generateHash());
}
return hashCode;
}
};
class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitknnUNetToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
QCache<size_t, nnUNetCache> m_Cache;
/**
* @brief The hash map stores all bifurcating processes' ID.
*
*/
std::unordered_map<std::string, mitk::ProcessExecutor::Pointer> m_Processes;
protected slots:
/**
* @brief Qt slot
*
*/
void OnPreviewRequested();
/**
* @brief Qt slot
*
*/
void OnDirectoryChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnModelChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnTaskChanged(const QString &);
/**
* @brief Qt slot
*
*/
void OnTrainerChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnCheckBoxChanged(int);
/**
* @brief Qthread slot to capture failures from thread worker and
* shows error message
*
*/
void SegmentationProcessFailed();
/**
- * @brief Qthread to capture sucessfull nnUNet segmentation.
+ * @brief Qthread to capture successful nnUNet segmentation.
* Further, renders the LabelSet image
*/
void SegmentationResultHandler(mitk::nnUNetTool *, bool forceRender = false);
/**
* @brief Qt Slot
*
*/
void OnModalitiesNumberChanged(int);
/**
* @brief Qt Slot
*
*/
void OnPythonPathChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnRefreshPresssed();
/**
* @brief Qt slot
*
*/
void OnClearCachePressed();
/**
* @brief Qt slot
*
*/
void OnDownloadModel();
/**
* @brief Qt slot
*
*/
void OnDownloadWorkerExit(bool, const QString);
/**
* @brief Qt slot
*
*/
void OnStopDownload();
signals:
/**
* @brief signal for starting the segmentation which is caught by a worker thread.
*/
void Operate(QString, QString, mitk::ProcessExecutor::Pointer, mitk::ProcessExecutor::ArgumentListType);
protected:
QmitknnUNetToolGUI();
~QmitknnUNetToolGUI();
void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override;
void InitializeUI(QBoxLayout* mainLayout) override;
void EnableWidgets(bool enabled) override;
private:
/**
* @brief Adds an element and hides it so that the ctkCheckableComboBox shows "None".
*/
void SetComboBoxToNone(ctkCheckableComboBox*);
/**
* @brief Parses the available_models.json file from RESULTS_FOLDER and loads
* the task names to the Download combobox in Advanced.
*/
void FillAvailableModelsInfoFromJSON(const QString&);
/**
* @brief Exports available models to download from nnUNet_print_available_pretrained_models
* output.
*/
void ExportAvailableModelsAsJSON(const QString&);
/**
* @brief Clears all displayed modal labels and widgets from GUI.
*
*/
void ClearAllModalities();
/**
* @brief Parses Json file containing modality info and populates
* labels and selection widgets accordingly on the GUI.
*/
void DisplayMultiModalInfoFromJSON(const QString&);
/**
* @brief Clears all modality labels previously populated from GUI.
*
*/
void ClearAllModalLabels();
/**
* @brief Runs a set of python commands to read "plans.pkl" and extract
* modality information required for inferencing. This information is exported
* as json file : "mitk_export.json".
*
* @return QString
*/
void DumpJSONfromPickle(const QString&);
/**
* @brief Searches RESULTS_FOLDER environment variable. If not found,
* returns from the QSettings stored last used path value.
* @return QString
*/
QString FetchResultsFolderFromEnv();
/**
* @brief Returns GPU id of the selected GPU from the Combo box.
*
* @return unsigned int
*/
unsigned int FetchSelectedGPUFromUI();
/**
* @brief Adds GPU information to the gpu combo box.
- * In case, there aren't any GPUs avaialble, the combo box will be
+ * In case, there aren't any GPUs available, the combo box will be
* rendered editable.
*/
void SetGPUInfo();
/**
* @brief Inserts the hash and segmentation into cache and
* updates count on UI.
*/
void AddToCache(size_t&, mitk::LabelSetImage::ConstPointer);
/**
* @brief Checks all the entries of the ctkCheckableComboBox ui widget.
* This feature is not present in ctkCheckableComboBox API.
*/
void CheckAllInCheckableComboBox(ctkCheckableComboBox*);
/**
* @brief Parses the folder names containing trainer and planner together and,
* returns it as separate lists.
* @return std::pair<QStringList, QStringList>
*/
std::pair<QStringList, QStringList> ExtractTrainerPlannerFromString(QStringList);
/**
* @brief Parses the ensemble UI elements and sets to nnUNetTool object pointer.
*
*/
void ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer);
/**
* @brief Parses the UI elements and sets to nnUNetTool object pointer.
*
*/
void ProcessModelParams(mitk::nnUNetTool::Pointer);
/**
* @brief Creates and renders QmitknnUNetTaskParamsUITemplate layout for ensemble input.
*/
void ShowEnsembleLayout(bool visible = true);
/**
* @brief Creates a QMessage object and shows on screen.
*/
void ShowErrorMessage(const std::string&, QMessageBox::Icon = QMessageBox::Critical);
/**
* @brief Writes any message in white on the tool pane.
*/
void WriteStatusMessage(const QString&);
/**
* @brief Writes any message in red on the tool pane.
*/
void WriteErrorMessage(const QString&);
/**
- * @brief Searches and parses paths of python virtual enviroments
+ * @brief Searches and parses paths of python virtual environments
* from predefined lookout locations
*/
void AutoParsePythonPaths();
/**
* @brief Check if pretrained model sub folder inside RESULTS FOLDER exist.
*/
bool IsModelExists(const QString&, const QString&, const QString&);
/**
* @brief Clears all combo boxes
* Any new combo box added in the future can be featured here for clearance.
*
*/
void ClearAllComboBoxes();
/**
* @brief Disable/deactivates the nnUNet GUI.
* Clears any multi modal labels and selection widgets, as well.
*/
void DisableEverything();
/**
* @brief Checks if nnUNet_predict command is valid in the selected python virtual environment.
*
* @return bool
*/
bool IsNNUNetInstalled(const QString&);
/**
* @brief Mapper function to map QString entries from UI to ModelParam attributes.
*
* @return mitk::ModelParams
*/
mitk::ModelParams MapToRequest(
const QString&, const QString&, const QString&, const QString&, const std::vector<std::string>&);
/**
* @brief Returns checked fold names from the ctk-Checkable-ComboBox.
*
* @return std::vector<std::string>
*/
std::vector<std::string> FetchSelectedFoldsFromUI(ctkCheckableComboBox*);
/**
* @brief Returns all paths from the dynamically generated ctk-path-line-edit boxes.
*
* @return std::vector<std::string>
*/
std::vector<mitk::Image::ConstPointer> FetchMultiModalImagesFromUI();
/**
* @brief Updates cache count on UI.
*
*/
void UpdateCacheCountOnUI();
Ui_QmitknnUNetToolGUIControls m_Controls;
QmitkGPULoader m_GpuLoader;
/**
* @brief Stores all dynamically added ctk-path-line-edit UI elements.
*
*/
std::vector<QmitkSingleNodeSelectionWidget*> m_Modalities;
std::vector<QLabel*> m_ModalLabels;
std::vector<std::unique_ptr<QmitknnUNetTaskParamsUITemplate>> m_EnsembleParams;
mitk::NodePredicateBase::Pointer m_MultiModalPredicate;
QString m_PythonPath;
/**
* @brief Stores row count of the "advancedSettingsLayout" layout element. This value helps dynamically add
* ctk-path-line-edit UI elements at the right place. Forced to initialize in the InitializeUI method since there is
* no guarantee of retrieving exact row count anywhere else.
*
*/
int m_UI_ROWS;
/**
* @brief Stores path of the model director (RESULTS_FOLDER appended by "nnUNet").
*
*/
std::shared_ptr<QmitknnUNetFolderParser> m_ParentFolder = nullptr;
/**
* @brief Valid list of models supported by nnUNet
*
*/
const QStringList m_VALID_MODELS = {"2d", "3d_lowres", "3d_fullres", "3d_cascade_fullres", "ensembles"};
const QString m_CACHE_COUNT_BASE_LABEL = "Cached Items: ";
const QString m_MITK_EXPORT_JSON_FILENAME = "mitk_export.json";
const QString m_AVAILABLE_MODELS_JSON_FILENAME = "available_models.json";
const QString m_PICKLE_FILENAME = "plans.pkl";
/**
* @brief For storing values across sessions. Currently, RESULTS_FOLDER value is cached using this.
*/
QSettings m_Settings;
bool m_IsResultsFolderValid = false;
QThread* m_nnUNetThread;
nnUNetDownloadWorker* m_Worker;
bool m_FirstPreviewComputation = true;
EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h
index c99aeb7a4c..f64b865380 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h
@@ -1,51 +1,51 @@
/*============================================================================
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 QmitknnUNetWorker_h
#define QmitknnUNetWorker_h
#include "mitkProcessExecutor.h"
#include <QMutex>
#include <QObject>
Q_DECLARE_METATYPE(mitk::ProcessExecutor::Pointer);
Q_DECLARE_METATYPE(mitk::ProcessExecutor::ArgumentListType);
/**
- * @brief Class to execute some functions from the Segmentation Plugin in a seperate thread
+ * @brief Class to execute some functions from the Segmentation Plugin in a separate thread
*/
class nnUNetDownloadWorker : public QObject
{
Q_OBJECT
public slots:
/**
* @brief Starts the download process worker thread.
*
*/
void DoWork(QString, QString, mitk::ProcessExecutor::Pointer, mitk::ProcessExecutor::ArgumentListType);
signals:
/**
- * @brief the signal emitted when a download process is finshed; success or failed
+ * @brief the signal emitted when a download process is finished; success or failed
*
* @param exitCode
* @param message
*/
void Exit(bool exitCode, const QString message);
private:
QMutex mutex;
};
#endif
diff --git a/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp b/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp
index d8b13f8ecf..38af470166 100644
--- a/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp
+++ b/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp
@@ -1,78 +1,78 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
#include "mitkTestingMacros.h"
#include <mitkImageToPointCloudFilter.h>
#include <mitkTestFixture.h>
#include <mitkIOUtil.h>
class mitkImageToPointCloudFilterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageToPointCloudFilterTestSuite);
MITK_TEST(testImageToPointCloudFilterInitialization);
MITK_TEST(testInput);
MITK_TEST(testUnstructuredGridGeneration);
MITK_TEST(testThreshold);
CPPUNIT_TEST_SUITE_END();
private:
/** Members used inside the different test methods. All members are initialized via setUp().*/
mitk::Image::Pointer m_BallImage;
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_BallImage = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("BallBinary30x30x30.nrrd")); }
void testImageToPointCloudFilterInitialization()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull());
}
void testInput()
{
mitk::ImageToPointCloudFilter::Pointer testFilter = mitk::ImageToPointCloudFilter::New();
testFilter->SetInput(m_BallImage);
CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_BallImage);
}
void testUnstructuredGridGeneration()
{
mitk::ImageToPointCloudFilter::Pointer testFilter = mitk::ImageToPointCloudFilter::New();
testFilter->SetInput(m_BallImage);
testFilter->Update();
CPPUNIT_ASSERT_MESSAGE("Testing UnstructuredGrid generation!", testFilter->GetOutput() != nullptr);
}
void testThreshold()
{
mitk::ImageToPointCloudFilter::Pointer testFilter1 = mitk::ImageToPointCloudFilter::New();
testFilter1->SetInput(m_BallImage);
testFilter1->Update();
int numberOfPoints1 = testFilter1->GetNumberOfExtractedPoints();
mitk::ImageToPointCloudFilter::Pointer testFilter2 = mitk::ImageToPointCloudFilter::New();
testFilter2->SetInput(m_BallImage);
testFilter2->SetMethod(mitk::ImageToPointCloudFilter::LAPLACIAN_STD_DEV3);
testFilter2->Update();
int numberOfPoints2 = testFilter2->GetNumberOfExtractedPoints();
CPPUNIT_ASSERT_MESSAGE("Testing Threshold", numberOfPoints1 > numberOfPoints2);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkImageToPointCloudFilter)
diff --git a/Modules/SurfaceInterpolation/mitkPlaneProposer.h b/Modules/SurfaceInterpolation/mitkPlaneProposer.h
index 3d823f68a4..e498f0dc83 100644
--- a/Modules/SurfaceInterpolation/mitkPlaneProposer.h
+++ b/Modules/SurfaceInterpolation/mitkPlaneProposer.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 mitkPlaneProposer_h
#define mitkPlaneProposer_h
#include <MitkSurfaceInterpolationExports.h>
#include <mitkPoint.h>
#include <mitkVector.h>
#include <itkSmartPointer.h>
#include <array>
#include <vector>
#include <mitkDataStorage.h>
namespace mitk
{
class SliceNavigationController;
class UnstructuredGrid;
/**
* @brief The PlaneProposer creates a new plane based on an input point cloud
*
* The proposal is either created by using the lease squares in order to fit
* a plane to the provided point cloud or by using the centroid of three clusters
*
* If less than three clusters are provided, least squares is chosen automatically.
* If the centroid method is chosed either the three biggest clusters are chosen by
* default. If the users sets PlaneProposer::SetUseDistances(true) the three clusters
* with the biggerst mean distance of all points are chosen. The latter requires the
* distances to be set as PointData scalar to the underlying VTK object.
*
* The user can either take the plane information or he can set a mitk::SliceNavigationController
* which will be used to automatically rotate to the suggested position.
*/
class MITKSURFACEINTERPOLATION_EXPORT PlaneProposer
{
public:
/**
* @brief Encapsulates the geometrical information needed to descripe a PlaneInfo
*
* normal = the normal of the plane
* x,y = the axes of the PlaneInfo
*/
struct PlaneInfo
{
mitk::Vector3D normal;
mitk::Vector3D x;
mitk::Vector3D y;
mitk::Point3D pointOnPlane;
};
PlaneProposer();
~PlaneProposer();
void SetUnstructuredGrids(std::vector<itk::SmartPointer<mitk::UnstructuredGrid>> &grids);
/**
* @brief If true, the three clusters with the biggest mean distances are used for plane proposal
* Required the distance for each point to be set in PointData scalars
*/
void SetUseDistances(bool);
/**
* @brief Tells the proposer to use least squares method for plane creating
*
* This will eb chosen automatically if less than three point clusters are provided
*/
void SetUseLeastSquares(bool);
/**
* @brief Sets the number of the clusters to be used for plane creation (default=3)
*/
void SetNumberOfClustersToUse(unsigned int);
void SetSliceNavigationController(itk::SmartPointer<mitk::SliceNavigationController> &snc);
/**
* @brief Creates the actual plane proposal
*
* Is less than three clusters are provide the least squares method will be chosen automatically
* The result will either be executed on a mitk::SliceNavigationController if provided or can be
* retrieved by calling mitk::PlaneProposer::GetProposedPlaneInfo()
*/
void CreatePlaneInfo();
PlaneInfo GetProposedPlaneInfo();
std::array<std::array<double, 3>, 3> GetCentroids();
private:
PlaneProposer(const PlaneProposer &); // not implemented on purpose
PlaneProposer &operator=(const PlaneProposer &); // not implemented on purpose
/**
- * @brief Creates a plane suggestion based on the cluster centriods
+ * @brief Creates a plane suggestion based on the cluster centroids
*/
PlaneInfo CreatePlaneByCentroids(const std::vector<std::pair<int, int>> &sizeIDs,
const std::vector<std::pair<double, int>> &avgDistances);
/**
* @brief Creates a plane suggestion based on the least squares
*/
PlaneInfo CreatePlaneByLeastSquares(const std::vector<std::pair<int, int>> &sizeIDs,
const std::vector<std::pair<double, int>> &avgDistances);
std::vector<itk::SmartPointer<mitk::UnstructuredGrid>> m_Grids;
bool m_UseDistances;
bool m_UseLeastSquares;
unsigned int m_NumberOfClustersToUse;
std::array<std::array<double, 3>, 3> m_Centroids;
itk::SmartPointer<mitk::SliceNavigationController> m_SNC;
PlaneInfo m_ProposedPlaneInfo;
};
} // namespace
#endif
diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
index c683d416af..f6d4c1c083 100644
--- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
+++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
@@ -1,259 +1,262 @@
/*============================================================================
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 mitkSurfaceInterpolationController_h
#define mitkSurfaceInterpolationController_h
#include <mitkDataStorage.h>
#include <mitkLabelSetImage.h>
#include <mitkLabel.h>
#include <mitkSurface.h>
#include <MitkSurfaceInterpolationExports.h>
namespace mitk
{
class ComputeContourSetNormalsFilter;
class CreateDistanceImageFromSurfaceFilter;
class LabelSetImage;
class ReduceContourSetFilter;
class MITKSURFACEINTERPOLATION_EXPORT SurfaceInterpolationController : public itk::Object
{
public:
mitkClassMacroItkParent(SurfaceInterpolationController, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
struct MITKSURFACEINTERPOLATION_EXPORT ContourPositionInformation
{
Surface::ConstPointer Contour;
PlaneGeometry::ConstPointer Plane;
Label::PixelType LabelValue;
TimeStepType TimeStep;
ContourPositionInformation()
: Plane(nullptr),
LabelValue(std::numeric_limits<Label::PixelType>::max()),
TimeStep(std::numeric_limits<TimeStepType>::max())
{
}
ContourPositionInformation(Surface::ConstPointer contour,
PlaneGeometry::ConstPointer plane,
Label::PixelType labelValue,
TimeStepType timeStep)
:
Contour(contour),
Plane(plane),
LabelValue(labelValue),
TimeStep(timeStep)
{
}
bool IsPlaceHolder() const
{
return Contour.IsNull();
}
};
typedef std::vector<ContourPositionInformation> CPIVector;
static SurfaceInterpolationController *GetInstance();
/**
* @brief Adds new extracted contours to the list. If one or more contours at a given position
* already exist they will be updated respectively
*/
void AddNewContours(const std::vector<ContourPositionInformation>& newCPIs, bool reinitializeAction = false, bool silent = false);
/**
* @brief Removes the contour for a given plane for the current selected segmentation
* @param contourInfo the contour which should be removed
* @param keepPlaceholderForUndo
* @return true if a contour was found and removed, false if no contour was found
*/
bool RemoveContour(ContourPositionInformation contourInfo, bool keepPlaceholderForUndo = false);
void RemoveObservers();
/**
* @brief Performs the interpolation.
*
*/
void Interpolate(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
/**
* @brief Get the Result of the interpolation operation.
*
* @return mitk::Surface::Pointer
*/
mitk::Surface::Pointer GetInterpolationResult(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
/**
* @brief Sets the minimum spacing of the current selected segmentation
* This is needed since the contour points we reduced before they are used to interpolate the surface.
*
* @param minSpacing Parameter to set
+ * @param minSpacing Parameter to set
*/
void SetMinSpacing(double minSpacing);
/**
* @brief Sets the minimum spacing of the current selected segmentation
* This is needed since the contour points we reduced before they are used to interpolate the surface
* @param maxSpacing Set the max Spacing for interpolation
*/
void SetMaxSpacing(double maxSpacing);
/**
* Sets the volume i.e. the number of pixels that the distance image should have
* By evaluation we found out that 50.000 pixel delivers a good result
*/
void SetDistanceImageVolume(unsigned int distImageVolume);
/**
* @brief Get the current selected segmentation for which the interpolation is performed
* @return the current segmentation image
*/
mitk::LabelSetImage* GetCurrentSegmentation();
void SetDataStorage(DataStorage::Pointer ds);
/**
* Sets the current list of contourpoints which is used for the surface interpolation
* @param currentSegmentationImage The current selected segmentation
*/
void SetCurrentInterpolationSession(LabelSetImage* currentSegmentationImage);
/**
* @brief Remove interpolation session
* @param segmentationImage the session to be removed
*/
void RemoveInterpolationSession(const LabelSetImage* segmentationImage);
/**
* @brief Removes all sessions
*/
void RemoveAllInterpolationSessions();
/**
* @brief Get the Contours at a certain timeStep and layerID.
*
* @param timeStep Time Step from which to get the contours.
* @param labelValue label from which to get the contours.
* @return std::vector<ContourPositionInformation> Returns contours.
*/
CPIVector* GetContours(LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
std::vector<LabelSetImage::LabelValueType> GetAffectedLabels(const LabelSetImage* seg, TimeStepType timeStep, const PlaneGeometry* plane) const;
/**
+ * @brief Triggered with the "Reinit Interpolation" action. The contours are used to repopulate the
* @brief Triggered with the "Reinit Interpolation" action. The contours are used to repopulate the
* surfaceInterpolator data structures so that interpolation can be performed after reloading data.
*/
void CompleteReinitialization(const std::vector<ContourPositionInformation>& newCPIs);
/**
* @brief Removes contours of a particular label and at a given time step for the current session/segmentation.
*
* @param segmentationImage
* @param label Label of contour to remove.
* @param timeStep Time step in which to remove the contours.
* @remark if the label or time step does not exist, nothing happens.
*/
void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label, TimeStepType timeStep);
/**
* @brief Removes contours of a particular label and at a given time step for the current session/segmentation.
*
* @param segmentationImage
* @param label Label of contour to remove.
* @remark if the label or time step does not exist, nothing happens.
*/
void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label);
unsigned int GetNumberOfInterpolationSessions();
/**
* @brief Get the Segmentation Image Node object
*
* @return DataNode* returns the DataNode containing the segmentation image.
*/
mitk::DataNode* GetSegmentationImageNode() const;
protected:
SurfaceInterpolationController();
~SurfaceInterpolationController() override;
template <typename TPixel, unsigned int VImageDimension>
void GetImageBase(itk::Image<TPixel, VImageDimension> *input, itk::ImageBase<3>::Pointer &result);
private:
/**
* @brief
*
* @param caller
* @param event
*/
void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event);
/**
+ * @brief Function that removes contours of a particular label when the "Remove Label" event is triggered in the labelSetImage.
* @brief Function that removes contours of a particular label when the "Remove Label" event is triggered in the labelSetImage.
*
*/
void OnRemoveLabel(const itk::Object* caller, const itk::EventObject& event);
/**
* @brief When a new contour is added to the pipeline or an existing contour is replaced,
* the plane geometry information of that contour is added as a child node to the
* current node of the segmentation image. This is useful in the retrieval of contour information
* when data is reloaded after saving.
*
* @param contourInfo contourInfo struct to add to data storage.
*/
void AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo) const;
DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode) const;
DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue) const;
DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) const;
/**
* Adds Contours from the active Label to the interpolation pipeline
*/
void AddActiveLabelContoursForInterpolation(ReduceContourSetFilter* reduceFilter, const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
/**
* @brief Clears the interpolation data structures. Called from CompleteReinitialization().
*
*/
void ClearInterpolationSession();
void RemoveObserversInternal(const mitk::LabelSetImage* segmentationImage);
/**
* @brief Add contour to the interpolation pipeline
*
* @param contourInfo Contour information to be added
* @param reinitializationAction If the contour is coming from a reinitialization process or not
*/
void AddToCPIMap(ContourPositionInformation& contourInfo, bool reinitializationAction = false);
unsigned int m_DistanceImageVolume;
mitk::DataStorage::Pointer m_DataStorage;
WeakPointer<LabelSetImage> m_SelectedSegmentation;
};
}
#endif
diff --git a/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp b/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp
index e1c1978719..038c2bcc9e 100644
--- a/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp
+++ b/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp
@@ -1,246 +1,246 @@
/*============================================================================
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 "QmitkHttpStatusCodeHandler.h"
#include "QMessageBox"
#include <mitkCommon.h>
#include <sstream>
QmitkHttpStatusCodeHandler::QmitkHttpStatusCodeHandler()
{
}
QmitkHttpStatusCodeHandler::~QmitkHttpStatusCodeHandler()
{
}
static void CreateMessageBox(int statusCode, std::string errorMessage)
{
std::stringstream ss;
// Qt uses different type of status code numbers.
switch (statusCode)
{
case 1:
QMessageBox::warning(nullptr,
"ConnectionRefusedError - 1",
"The remote server refused the connection (the server is not accepting requests).");
break;
case 2:
QMessageBox::warning(
nullptr,
"RemoteHostClosedError - 2",
"The remote server closed the connection prematurely, before the entire reply was received and processed.");
break;
case 3:
QMessageBox::warning(nullptr, "HostNotFoundError - 3", "The remote host name was not found (invalid hostname).");
break;
case 4:
QMessageBox::warning(nullptr, "TimeoutError - 4", "The connection to the remote server timed out.");
break;
case 5:
QMessageBox::warning(nullptr,
"OperationCanceledError - 5",
"The operation was canceled via calls to abort() or close() before it was finished.");
break;
case 6:
QMessageBox::warning(nullptr, "SslHandshakeFailedError - 6", "The SSL/TLS handshake failed and the encrypted "
"channel could not be established. The sslErrors() "
"signal should have been emitted.");
break;
case 7:
QMessageBox::warning(nullptr,
"TemporaryNetworkFailureError - 7",
"The connection was broken due to disconnection from the network, however the system has "
"initiated roaming to another access point. The request should be resubmitted and will be "
"processed as soon as the connection is re-established.");
break;
case 8:
QMessageBox::warning(
nullptr,
"NetworkSessionFailedError - 8",
"The connection was broken due to disconnection from the network or failure to start the network.");
break;
case 9:
QMessageBox::warning(nullptr,
"BackgroundRequestNotAllowedError - 9",
"The background request is not currently allowed due to platform policy.");
break;
case 10:
QMessageBox::warning(nullptr, "TooManyRedirectsError - 10 ", "While following redirects, the maximum limit was "
"reached. The limit is by default set to 50 or as "
"set by QNetworkRequest::setMaxRedirectsAllowed().");
break;
case 11:
QMessageBox::warning(nullptr, "InsecureRedirectError - 11 ", "While following redirects, the network access API "
"detected a redirect from a encrypted protocol "
"(https) to an unencrypted one (http).");
break;
case 101:
QMessageBox::warning(
nullptr,
"ProxyConnectionRefusedError - 101",
"The connection to the proxy server was refused (the proxy server is not accepting requests).");
break;
case 102:
QMessageBox::warning(
nullptr,
"ProxyConnectionClosedError - 102",
"The proxy server closed the connection prematurely, before the entire reply was received and processed.");
break;
case 103:
QMessageBox::warning(
nullptr, "ProxyNotFoundError - 103", "The proxy host name was not found (invalid proxy hostname).");
break;
case 104:
QMessageBox::warning(
nullptr,
"ProxyTimeoutError - 104",
"The connection to the proxy timed out or the proxy did not reply in time to the request sent.");
break;
case 105:
QMessageBox::warning(nullptr, "ProxyAuthenticationRequiredError - 105", "The proxy requires authentication in "
"order to honour the request but did not "
"accept any credentials offered (if "
"any).");
break;
case 201:
QMessageBox::warning(nullptr,
"ContentAccessDenied - 201",
"The access to the remote content was denied (similar to HTTP error 401).");
break;
case 202:
QMessageBox::warning(nullptr,
"ContentOperationNotPermittedError - 202",
"The operation requested on the remote content is not permitted.");
break;
case 203:
QMessageBox::warning(nullptr,
"ContentNotFoundError - 203",
"The remote content was not found at the server (similar to HTTP error 404).");
break;
case 204:
QMessageBox::warning(nullptr, "AuthenticationRequiredError - 204", "The remote server requires authentication to "
"serve the content but the credentials "
"provided were not accepted (if any).");
break;
case 205:
QMessageBox::warning(nullptr, "ContentReSendError - 205", "The request needed to be sent again, but this failed "
"for example because the upload data could not be read "
"a second time.");
break;
case 206:
QMessageBox::warning(
nullptr,
"ContentConflictError - 206",
"The request could not be completed due to a conflict with the current state of the resource.");
break;
case 207:
QMessageBox::warning(
nullptr, "ContentGoneError - 207", "The requested resource is no longer available at the server.");
break;
case 401:
QMessageBox::warning(
nullptr,
"InternalServerError - 401",
"The server encountered an unexpected condition which prevented it from fulfilling the request.");
break;
case 402:
QMessageBox::warning(nullptr,
"OperationNotImplementedError - 402",
"The server does not support the functionality required to fulfill the request.");
break;
case 403:
QMessageBox::warning(
nullptr, "ServiceUnavailableError - 403", "The server is unable to handle the request at this time.");
break;
case 301:
QMessageBox::warning(nullptr,
"ProtocolUnknownError - 301",
"The Network Access API cannot honor the request because the protocol is not known.");
break;
case 302:
QMessageBox::warning(
nullptr, "ProtocolInvalidOperationError - 302", "The requested operation is invalid for this protocol.");
break;
case 99:
QMessageBox::warning(nullptr, "UnknownNetworkError - 99", "An unknown network-related error was detected.");
break;
case 199:
QMessageBox::warning(nullptr, "UnknownProxyError - 199", "An unknown proxy-related error was detected.");
break;
case 299:
QMessageBox::warning(
nullptr, "UnknownContentError - 299", "An unknown error related to the remote content was detected.");
break;
case 399:
QMessageBox::warning(
nullptr,
"ProtocolFailure - 399",
"A breakdown in protocol was detected (parsing error, invalid or unexpected responses, etc.).");
break;
case 499:
QMessageBox::warning(
nullptr, "UnknownServerError - 499", "An unknown error related to the server response was detected.");
break;
default:
- ss << "An Http Error occured with error code " << statusCode << " and server message: " << errorMessage;
+ ss << "An Http Error occurred with error code " << statusCode << " and server message: " << errorMessage;
QMessageBox::warning(nullptr, "HTTP ERROR", ss.str().c_str());
break;
}
}
bool QmitkHttpStatusCodeHandler::HandleErrorMessage(const char *_errorMsg)
{
static int lastCode = 0;
std::string errorMsg(_errorMsg, strnlen(_errorMsg, strlen(_errorMsg)));
bool success = true;
/*
* sample error response:
* ERROR: An error occurred: ctkRuntimeException: Syncing with http request failed.
* {d55ec279-8a65-46d6-80d3-cec079066109}: 202: Error downloading
* https:... - server replied: Forbidden
*/
if (errorMsg.find("request failed.") == std::string::npos)
success = false;
std::string::size_type indexOfErrorCode = errorMsg.find(": Error") - 3;
std::string::size_type indexOfServerResponse =
errorMsg.rfind("server replied: ") + 16; // Length of "server replied : " is 16
if (indexOfErrorCode == std::string::npos || indexOfServerResponse == std::string::npos)
success = false;
std::string statusCodeString = errorMsg.substr(indexOfErrorCode, 3);
std::stringstream str;
str << statusCodeString;
int statusCode;
str >> statusCode;
std::string serverResponse = errorMsg.substr(indexOfServerResponse);
if (lastCode != statusCode)
::CreateMessageBox(statusCode, serverResponse);
if (!success && lastCode != statusCode)
{
QMessageBox::warning(nullptr, "General Error", errorMsg.c_str());
}
if (lastCode != statusCode)
lastCode = statusCode;
else
lastCode = 0;
return success;
}

File Metadata

Mime Type
application/octet-stream
Expires
Tue, Oct 1, 4:13 AM (2 d)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
7fvqMk3ALnyg
Default Alt Text
(4 MB)

Event Timeline