diff --git a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp index 323c74bc00..87269991c4 100644 --- a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp +++ b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp @@ -1,500 +1,500 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTransformationOperation.h" #include #include #include #include #include #include // Wavelet #include #include #include #include #include #include #include #include #include #include "itkZeroFluxNeumannBoundaryCondition.h" #include "itkPeriodicBoundaryCondition.h" #include "itkConstantBoundaryCondition.h" //#include #include "itkCastImageFilter.h" #include "itkUnaryFunctorImageFilter.h" #include -#include +#include #include namespace mitk { namespace Functor { template< class TInput> class ThresholdValue { public: ThresholdValue() {}; ~ThresholdValue() {}; bool operator!=(const ThresholdValue &) const { return false; } bool operator==(const ThresholdValue & other) const { return !(*this != other); } inline unsigned short operator()(const TInput & A) const { if (A < value) return 0; else return 1; } double value = 0.0; }; template< class TInput, class TOutput> class RoundValue { public: RoundValue() {}; ~RoundValue() {}; bool operator!=(const RoundValue &) const { return false; } bool operator==(const RoundValue & other) const { return !(*this != other); } inline TOutput operator()(const TInput & A) const { return std::round(A); } }; } } template static void ExecuteMultiResolution(itk::Image* image, unsigned int numberOfLevels, bool outputAsDouble, std::vector &resultImages) { typedef itk::Image ImageType; typedef itk::Image DoubleOutputType; typedef itk::RecursiveMultiResolutionPyramidImageFilter ImageTypeFilterType; typedef itk::RecursiveMultiResolutionPyramidImageFilter DoubleTypeFilterType; if (outputAsDouble) { typename DoubleTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = DoubleTypeFilterType::New(); recursiveMultiResolutionPyramidImageFilter->SetInput(image); recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels); recursiveMultiResolutionPyramidImageFilter->Update(); // This outputs the levels (0 is the lowest resolution) for (unsigned int i = 0; i < numberOfLevels; ++i) { mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage); resultImages.push_back(outputImage); } } else { typename ImageTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = ImageTypeFilterType::New(); recursiveMultiResolutionPyramidImageFilter->SetInput(image); recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels); recursiveMultiResolutionPyramidImageFilter->Update(); // This outputs the levels (0 is the lowest resolution) for (unsigned int i = 0; i < numberOfLevels; ++i) { mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage); resultImages.push_back(outputImage); } } } std::vector mitk::TransformationOperation::MultiResolution(Image::Pointer & image, unsigned int numberOfLevels, bool outputAsDouble) { std::vector resultImages; AccessByItk_n(image, ExecuteMultiResolution, (numberOfLevels, outputAsDouble, resultImages)); return resultImages; } template static void ExecuteLaplacianOfGaussian(itk::Image* image, double sigma, bool outputAsDouble, mitk::Image::Pointer &resultImage) { typedef itk::Image ImageType; typedef itk::Image DoubleOutputType; typedef itk::LaplacianRecursiveGaussianImageFilter ImageTypeFilterType; typedef itk::LaplacianRecursiveGaussianImageFilter DoubleTypeFilterType; if (outputAsDouble) { typename DoubleTypeFilterType::Pointer filter = DoubleTypeFilterType::New(); filter->SetInput(image); filter->SetSigma(sigma); filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } else { typename ImageTypeFilterType::Pointer filter = ImageTypeFilterType::New(); filter->SetInput(image); filter->SetSigma(sigma); filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } } mitk::Image::Pointer mitk::TransformationOperation::LaplacianOfGaussian(Image::Pointer & image, double sigma, bool outputAsDouble) { Image::Pointer resultImage; AccessByItk_n(image, ExecuteLaplacianOfGaussian, (sigma, outputAsDouble, resultImage)); return resultImage; } template static void ExecuteSpecificWaveletTransformation(itk::Image* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, std::vector &resultImages) { const unsigned int Dimension = VImageDimension; typedef TInputPixel PixelType; typedef TOutputPixel OutputPixelType; typedef itk::Image< PixelType, Dimension > ImageType; typedef itk::Image< double, Dimension > DoubleImageType; typedef itk::Image< OutputPixelType, Dimension > OutputImageType; typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType; typedef itk::FFTPadPositiveIndexImageFilter< DoubleImageType > FFTPadType; typedef itk::ForwardFFTImageFilter< DoubleImageType, itk::Image< std::complex, Dimension> > FFTFilterType; typedef typename FFTFilterType::OutputImageType ComplexImageType; typedef TWaveletFunction WaveletFunctionType; typedef itk::WaveletFrequencyFilterBankGenerator< ComplexImageType, WaveletFunctionType > WaveletFilterBankType; typedef itk::WaveletFrequencyForward< ComplexImageType, ComplexImageType, WaveletFilterBankType > ForwardWaveletType; typedef itk::InverseFFTImageFilter< ComplexImageType, OutputImageType > InverseFFTFilterType; // Convert input parameter unsigned int highSubBands = numberOfBands; //inputBands; unsigned int levels = numberOfLevels; // Perform FFT on input image typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(image); // Pad Image so it fits the expect typename FFTPadType::Pointer fftpad = FFTPadType::New(); fftpad->SetSizeGreatestPrimeFactor(4); itk::ConstantBoundaryCondition< DoubleImageType > constantBoundaryCondition; itk::PeriodicBoundaryCondition< DoubleImageType > periodicBoundaryCondition; itk::ZeroFluxNeumannBoundaryCondition< DoubleImageType > zeroFluxNeumannBoundaryCondition; switch (condition) { case mitk::BorderCondition::Constant: fftpad->SetBoundaryCondition(&constantBoundaryCondition); break; case mitk::BorderCondition::Periodic: fftpad->SetBoundaryCondition(&periodicBoundaryCondition); break; case mitk::BorderCondition::ZeroFluxNeumann: fftpad->SetBoundaryCondition(&zeroFluxNeumannBoundaryCondition); break; default: break; } fftpad->SetInput(castFilter->GetOutput()); typename FFTFilterType::Pointer fftFilter = FFTFilterType::New(); fftFilter->SetInput(fftpad->GetOutput()); // Calculate forward transformation typename ForwardWaveletType::Pointer forwardWavelet = ForwardWaveletType::New(); forwardWavelet->SetHighPassSubBands(highSubBands); forwardWavelet->SetLevels(levels); forwardWavelet->SetInput(fftFilter->GetOutput()); forwardWavelet->Update(); // Obtain target spacing, size and origin typename ComplexImageType::SpacingType inputSpacing; for (unsigned int i = 0; i < Dimension; ++i) { inputSpacing[i] = image->GetLargestPossibleRegion().GetSize()[i]; } typename ComplexImageType::SpacingType expectedSpacing = inputSpacing; typename ComplexImageType::PointType inputOrigin = image->GetOrigin(); typename ComplexImageType::PointType expectedOrigin = inputOrigin; typename ComplexImageType::SizeType inputSize = fftFilter->GetOutput()->GetLargestPossibleRegion().GetSize(); typename ComplexImageType::SizeType expectedSize = inputSize; // Inverse FFT to obtain filtered images typename InverseFFTFilterType::Pointer inverseFFT = InverseFFTFilterType::New(); for (unsigned int level = 0; level < numberOfLevels + 1; ++level) { double scaleFactorPerLevel = std::pow(static_cast< double >(forwardWavelet->GetScaleFactor()),static_cast< double >(level)); for (unsigned int i = 0; i < Dimension; ++i) { expectedSize[i] = inputSize[i] / scaleFactorPerLevel; expectedOrigin[i] = inputOrigin[i]; expectedSpacing[i] = inputSpacing[i] * scaleFactorPerLevel; } for (unsigned int band = 0; band < highSubBands; ++band) { unsigned int nOutput = level * forwardWavelet->GetHighPassSubBands() + band; // Do not compute bands in low-pass level. if (level == numberOfLevels && band == 0) { nOutput = forwardWavelet->GetTotalOutputs() - 1; } else if (level == numberOfLevels && band != 0) { break; } inverseFFT->SetInput(forwardWavelet->GetOutput(nOutput)); inverseFFT->Update(); auto itkOutputImage = inverseFFT->GetOutput(); itkOutputImage->SetSpacing(expectedSpacing); mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(itkOutputImage, outputImage); resultImages.push_back(outputImage); } } } template static void ExecuteWaveletTransformation(itk::Image* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType, std::vector &resultImages) { typedef itk::Point< double, VImageDimension > PointType; typedef itk::HeldIsotropicWavelet< double, VImageDimension, PointType > HeldIsotropicWaveletType; typedef itk::VowIsotropicWavelet< double, VImageDimension, PointType > VowIsotropicWaveletType; typedef itk::SimoncelliIsotropicWavelet< double, VImageDimension, PointType > SimoncelliIsotropicWaveletType; typedef itk::ShannonIsotropicWavelet< double, VImageDimension, PointType > ShannonIsotropicWaveletType; switch (waveletType) { case mitk::WaveletType::Held: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Shannon: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Simoncelli: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Vow: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; default: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; } } std::vector mitk::TransformationOperation::WaveletForward(Image::Pointer & image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType) { std::vector resultImages; AccessByItk_n(image, ExecuteWaveletTransformation, (numberOfLevels, numberOfBands, condition, waveletType, resultImages)); return resultImages; } template static void ExecuteImageTypeToDouble(itk::Image* image, mitk::Image::Pointer &outputImage) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< double, VImageDimension > DoubleImageType; typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType; typedef itk::ImageDuplicator< DoubleImageType > DuplicatorType; // Perform FFT on input image typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(image); castFilter->Update(); typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(castFilter->GetOutput()); duplicator->Update(); CastToMitkImage(duplicator->GetOutput(), outputImage); } template static void ExecuteRoundImage(itk::Image* /*image*/, mitk::Image::Pointer resampledImage, mitk::Image::Pointer &outputImage) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< double, VImageDimension > DoubleImageType; typedef itk::UnaryFunctorImageFilter< DoubleImageType, ImageType, mitk::Functor::RoundValue > DefaultFilterType; typename DoubleImageType::Pointer itkImage = DoubleImageType::New(); mitk::CastToItkImage(resampledImage, itkImage); typename DefaultFilterType::Pointer filter = DefaultFilterType::New(); filter->SetInput(itkImage); filter->Update(); CastToMitkImage(filter->GetOutput(), outputImage); } mitk::Image::Pointer mitk::TransformationOperation::ResampleImage(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position, bool returnAsDouble, bool roundOutput) { // Convert image to double if required mitk::Image::Pointer tmpImage = image; if (returnAsDouble) { AccessByItk_n(image, ExecuteImageTypeToDouble, (tmpImage)); } auto newGeometry = image->GetGeometry()->Clone(); mitk::Vector3D spacing; mitk::BaseGeometry::BoundsArrayType bounds = newGeometry->GetBounds(); for (int i = 0; i < 3; ++i) { spacing[i] = newGeometry->GetSpacing()[i]; //bounds[i*2+1] = newGeometry->GetBounds()[i * 2 + 1]; if (spacingVector[i] > 0) { spacing[i] = spacingVector[i]; if (position == mitk::GridInterpolationPositionType::SameSize) { unsigned int samples = image->GetDimensions()[i]; double currentSpacing = newGeometry->GetSpacing()[i]; double newFactor = std::floor(samples*currentSpacing / spacingVector[i]); spacing[i] = samples * currentSpacing / newFactor; } } bounds[i * 2] = 0; bounds[i*2+1] = std::ceil(bounds[i*2+1] * newGeometry->GetSpacing()[i] *1.0 / spacing[i]); } mitk::Point3D origin = newGeometry->GetOrigin(); if (position == mitk::GridInterpolationPositionType::CenterAligned) { for (int i = 0; i < 3; ++i) { double oldLength = newGeometry->GetSpacing()[i] * newGeometry->GetBounds()[i*2+1]; double newLength = spacing[i] * bounds[i*2+1]; origin[i] = origin[i] - (newLength - oldLength) / 2; } } newGeometry->SetSpacing(spacing); newGeometry->SetOrigin(origin); newGeometry->SetBounds(bounds); mitk::Image::Pointer tmpResult = ImageMappingHelper::map( tmpImage, mitk::GenerateIdentityRegistration3D().GetPointer(), false, 0.0, //Padding Value newGeometry.GetPointer(), false, 0, //Error Value interpolator ); mitk::Image::Pointer result = mitk::Image::New(); if (roundOutput) { AccessByItk_n(tmpImage, ExecuteRoundImage, (tmpResult, result)); } else { result = tmpResult; } return result; } template static void ExecuteImageThresholding(itk::Image* image, mitk::Image::Pointer &resultImage) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::UnaryFunctorImageFilter< ImageType, MaskType, mitk::Functor::ThresholdValue > DefaultFilterType; typename DefaultFilterType::Pointer filter = DefaultFilterType::New(); filter->SetInput(image); filter->GetFunctor().value = 0.5; filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } mitk::Image::Pointer mitk::TransformationOperation::ResampleMask(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position) { mitk::Image::Pointer result; if (interpolator == mitk::ImageMappingInterpolator::NearestNeighbor) { result = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, false, false); } else { auto tmpResult = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, true, false); AccessByItk_n(tmpResult, ExecuteImageThresholding, (result)); } return result; } namespace itk { namespace utils { IndexPairType IndexToLevelBandSteerablePyramid(unsigned int linearIndex, unsigned int levels, unsigned int bands) { unsigned int totalOutputs = 1 + levels * bands; if (linearIndex > totalOutputs - 1) { itkGenericExceptionMacro(<< "Failed converting linearIndex " << linearIndex << " with levels: " << levels << " bands: " << bands << " to Level,Band pair : out of bounds"); } // Low pass (band = 0). if (linearIndex == totalOutputs - 1) { return std::make_pair(levels - 1, 0); } unsigned int band = (linearIndex) % bands + 1; // note integer division ahead. unsigned int level = (linearIndex) / bands; itkAssertInDebugAndIgnoreInReleaseMacro(level < levels); return std::make_pair(level, band); } // Instantiation template unsigned int ComputeMaxNumberOfLevels<3>(const Size< 3 >& inputSize, const unsigned int & scaleFactor); template unsigned int ComputeMaxNumberOfLevels<2>(const Size< 2 >& inputSize, const unsigned int & scaleFactor); } // end namespace utils } // end namespace itk diff --git a/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp b/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp index 2cab1b9183..2e29e11d93 100644 --- a/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp +++ b/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp @@ -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. ============================================================================*/ #include "mitkProperties.h" #include "mitkCommandLineParser.h" #include "mitkIOUtil.h" #include "mitkPreferenceListReaderOptionsFunctor.h" // MatchPoint #include #include #include #include #include #include #include #include #include #include #include -#include +#include // Qt #include #include #include //#include #include #include #include #include int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Dicom Loader"); parser.setCategory("Preprocessing 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("moving", "m", mitkCommandLineParser::Directory, "Input folder:", "Input folder", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("fixed", "f", mitkCommandLineParser::Directory, "Input folder:", "Input folder", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file:", "Output file", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.addArgument("reader", "r", mitkCommandLineParser::Int, "Reader ID", "Reader Name", us::Any(), false); parser.addArgument("interpolation", "interp", mitkCommandLineParser::Int, "Reader ID", "Reader Name", us::Any(), false); std::map parsedArgs = parser.parseArguments(argc, argv); QFileInfo fi(argv[0]); map::deployment::DLLDirectoryBrowser::Pointer browser = map::deployment::DLLDirectoryBrowser::New(); browser->addDLLSearchLocation(QDir::homePath().toStdString()); browser->addDLLSearchLocation(QDir::currentPath().toStdString()); browser->addDLLSearchLocation(fi.canonicalPath().toStdString()); browser->update(); auto dllList = browser->getLibraryInfos(); int id = 0; std::cout << std::endl << " --- Algorithm List --- " << std::endl; for (auto info : dllList) { std::cout << "Algorithm ID " << id << ": " << info->getAlgorithmUID().getName() << std::endl; ++id; } std::cout << std::endl << " --- Interpolation List --- " << std::endl; std::cout << "Interpolation ID 0: Linear Interpolation " << std::endl; std::cout << "Interpolation ID 1: Nearest Neighbour" << std::endl; std::cout << "Interpolation ID 2: BSpline 3D" << std::endl << std::endl; mitk::ImageMappingInterpolator::Type interpolationMode = mitk::ImageMappingInterpolator::Linear; if (parsedArgs.size()==0) return EXIT_FAILURE; // Show a help message if ( parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } std::string movingFile = us::any_cast(parsedArgs["moving"]); std::string fixedFile = us::any_cast(parsedArgs["fixed"]); int selectedAlgorithm = us::any_cast(parsedArgs["reader"]); std::string outputPath = us::any_cast(parsedArgs["output"]); if (parsedArgs.count("interpolation")) { switch (us::any_cast(parsedArgs["interpolation"])) { case 0: interpolationMode = mitk::ImageMappingInterpolator::Linear; break; case 1: interpolationMode = mitk::ImageMappingInterpolator::NearestNeighbor; break; case 2: interpolationMode = mitk::ImageMappingInterpolator::BSpline_3; break; default: interpolationMode = mitk::ImageMappingInterpolator::Linear; } } mitk::Image::Pointer movingImage = mitk::IOUtil::Load(movingFile); mitk::Image::Pointer fixedImage = mitk::IOUtil::Load(fixedFile); auto dllInfo = dllList[selectedAlgorithm]; if (!dllInfo) { MITK_ERROR << "No valid algorithm is selected. Cannot load algorithm. ABORTING."; return -1; } ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( dllInfo->getLibraryFilePath()); ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); MITK_INFO << "Well...."; if (tempAlgorithm.IsNull()) { MITK_ERROR << "Error. Cannot load selected algorithm."; return -2; } - mitk::MITKAlgorithmHelper helper(tempAlgorithm); + mitk::MAPAlgorithmHelper helper(tempAlgorithm); helper.SetData(movingImage, fixedImage); auto registration = helper.GetRegistration(); MITK_INFO << "Well...."; mitk::Image::Pointer spResultData= mitk::ImageMappingHelper::map(movingImage, registration, false, // Use all Pixels 0.0, // Padding Value fixedImage->GetGeometry()->Clone().GetPointer(), // Ref. Geometry false, //!(this->m_allowUnregPixels), 0, // Error Value interpolationMode // Interpolator Type ); MITK_INFO << "Well...."; mitk::IOUtil::Save(spResultData, outputPath); return EXIT_SUCCESS; } diff --git a/Modules/Core/include/mitkBaseData.h b/Modules/Core/include/mitkBaseData.h index 21177dffe6..539a9e35fd 100644 --- a/Modules/Core/include/mitkBaseData.h +++ b/Modules/Core/include/mitkBaseData.h @@ -1,417 +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 BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include "mitkOperationActor.h" #include "mitkPropertyList.h" #include "mitkTimeGeometry.h" #include namespace mitk { // class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo + //## @remark Some derived classes may support the persistence of the Identifiable UID. + //** but it is no guaranteed feature and also depends on the format the data is stored in + //** as not all formats support storing of meta information. Please check the documentation + //** of the IFileReader and IFileWriter classes to see if the ID-persistance is supported. + //** MITK SceneIO supports the UID persistance for all BaseData derived classes. //## @ingroup Data class MITKCORE_EXPORT BaseData : public itk::DataObject, public OperationActor, public Identifiable, public IPropertyOwner { public: mitkClassMacroItkParent(BaseData, itk::DataObject); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ const mitk::TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry.GetPointer(); } /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. * \deprecatedSince{2013_09} Please use GetTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry *GetTimeSlicedGeometry() const) { return GetTimeGeometry(); } /** * @brief Return the TimeGeometry of the data as pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ mitk::TimeGeometry *GetTimeGeometry() { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). */ const mitk::TimeGeometry *GetUpdatedTimeGeometry(); /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry *GetUpdatedTimeSliceGeometry()) { return GetUpdatedTimeGeometry(); } /** * \brief Expands the TimeGeometry to a number of TimeSteps. * * The method expands the TimeGeometry to the given number of TimeSteps, * filling newly created elements with empty geometries. Sub-classes should override * this method to handle the elongation of their data vectors, too. * Note that a shrinking is neither possible nor intended. */ virtual void Expand(unsigned int timeSteps); /** * \brief Return the BaseGeometry of the data at time \a t. * * The method does not simply return * m_TimeGeometry->GetGeometry(t). * Before doing this, it makes sure that the BaseGeometry is up-to-date * (by setting the update extent appropriately and calling * UpdateOutputInformation). * * @todo Appropriate setting of the update extent is missing. */ const mitk::BaseGeometry *GetUpdatedGeometry(int t = 0); //##Documentation //## @brief Return the geometry, which is a TimeGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::BaseGeometry *GetGeometry(int t = 0) const { if (m_TimeGeometry.IsNull()) return nullptr; return m_TimeGeometry->GetGeometryForTimeStep(t); } //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation() override; //##Documentation //## @brief Set the RequestedRegion to the LargestPossibleRegion. //## //## This forces a filter to produce all of the output in one execution //## (i.e. not streaming) on the next call to Update(). void SetRequestedRegionToLargestPossibleRegion() override = 0; //##Documentation //## @brief Determine whether the RequestedRegion is outside of the BufferedRegion. //## //## This method returns true if the RequestedRegion //## is outside the BufferedRegion (true if at least one pixel is //## outside). This is used by the pipeline mechanism to determine //## whether a filter needs to re-execute in order to satisfy the //## current request. If the current RequestedRegion is already //## inside the BufferedRegion from the previous execution (and the //## current filter is up to date), then a given filter does not need //## to re-execute bool RequestedRegionIsOutsideOfTheBufferedRegion() override = 0; //##Documentation //## @brief Verify that the RequestedRegion is within the LargestPossibleRegion. //## //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. bool VerifyRequestedRegion() override = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject *data) override; //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmptyTimeStep(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. //## //## This method is implemented in the concrete subclasses of BaseData. void SetRequestedRegion(const itk::DataObject *data) override = 0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation *operation) override; /** * \brief Set the BaseGeometry of the data, which will be referenced (not copied!). * Assumes the data object has only 1 time step ( is a 3D object ) and creates a * new TimeGeometry which saves the given BaseGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedGeometry */ virtual void SetGeometry(BaseGeometry *aGeometry3D); /** * \brief Set the TimeGeometry of the data, which will be referenced (not copied!). * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedTimeGeometry */ virtual void SetTimeGeometry(TimeGeometry *geometry); /** * \brief Set a clone of the provided Geometry as Geometry of the data. * Assumes the data object has only 1 time step ( is a 3D object ) and * creates a new TimeGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * \sa SetGeometry */ virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * * \sa SetGeometry */ virtual void SetClonedTimeGeometry(const TimeGeometry *geometry); //##Documentation //## @brief Set a clone of the provided geometry as BaseGeometry of a given time step. //## //## \sa SetGeometry virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList *propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty *property); //##Documentation //## @brief Convenience method for setting the origin of //## the BaseGeometry instances of all time steps //## //## \warning Geometries contained in the BaseGeometry will //## \em not be changed, e.g. in case the BaseGeometry is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D &origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointer and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointer GetSource() const; //##Documentation //## @brief Get the number of time steps from the TimeGeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); } //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. unsigned long GetMTime() const override; /** * \sa itk::ProcessObject::Graft */ void Graft(const DataObject *) override; protected: BaseData(); BaseData(const BaseData &other); ~BaseData() override; //##Documentation //## \brief Initialize the TimeGeometry for a number of time steps. //## The TimeGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeGeometry(unsigned int timeSteps = 1); /** * \brief Initialize the TimeGeometry for a number of time steps. * The TimeGeometry is initialized empty and evenly timed. * In many cases it will be necessary to overwrite this in sub-classes. * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(virtual void InitializeTimeSlicedGeometry(unsigned int timeSteps = 1)) { InitializeTimeGeometry(timeSteps); } //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry virtual void InitializeEmpty() {} void PrintSelf(std::ostream &os, itk::Indent indent) const override; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable unsigned int m_SourceOutputIndexDuplicate; bool m_Initialized; private: //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeGeometry::Pointer m_TimeGeometry; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Modules/Core/include/mitkIdentifiable.h b/Modules/Core/include/mitkIdentifiable.h index 1d6412cfc7..9c2cef821f 100644 --- a/Modules/Core/include/mitkIdentifiable.h +++ b/Modules/Core/include/mitkIdentifiable.h @@ -1,60 +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 mitkIdentifiable_h #define mitkIdentifiable_h #include #include namespace mitk { /** * \brief Base class of identifiable objects. * - * If you want to change the unique ID after creation time, you can use + * Offers an interface to query a UID for the instance. Can be inherited + * by other classes to provide this capability.\n\n + * What does the UID stand for/what is its scope?\n + * - It is unique in its creation, but it is not a content specific unique ID. Therfore: + * - A class instance, associated with a UID, may change its values over its lifetime, + but still have the same UID. + - If a class instance gets persisted and loaded multiple times, then their could + be several instances with the same UID. + - UIDs are therefore more simelar to git paths than to git hashes. They identify something, but the state of + something can change (stream of commits). The difference of the UID compared to using e.g. plain instance + pointers to identify an object is that UIDs allow the feature of serialization as they abstract from the + memory location of the current runtime environment. + - It is up to the application that builds upon that feature to ensure appropriate usage within + the application scope. + * @remark It is not a feature of mitk::Identifiable per se to be persistable. It depends on classes that derive + * from mitk::Identifiable, if and how they implement a persistence mechanism for their MITK UID. + * @remark If you want to change the unique ID after creation time, you can use * the mitk::UIDManipulator class. The reationale behind this pattern is * to ensure that you think twice before doing this. It is intended to be * used by data readers if necessary at all. */ class MITKCORE_EXPORT Identifiable { public: using UIDType = std::string; Identifiable(); explicit Identifiable(const UIDType &uid); Identifiable(const Identifiable &) = delete; Identifiable(Identifiable &&) noexcept; virtual ~Identifiable(); Identifiable & operator =(const Identifiable &) = delete; Identifiable & operator =(Identifiable &&other) noexcept; /** * \brief Get unique ID of an object. * * \return Empty string if an object has no unique ID. */ - UIDType GetUID() const; + virtual UIDType GetUID() const; + + protected: + virtual void SetUID(const UIDType& uid); private: friend class UIDManipulator; - void SetUID(const UIDType &uid); - struct Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/Core/include/mitkItkImageIO.h b/Modules/Core/include/mitkItkImageIO.h index cefc222130..861243eebc 100644 --- a/Modules/Core/include/mitkItkImageIO.h +++ b/Modules/Core/include/mitkItkImageIO.h @@ -1,79 +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 MITKITKFILEIO_H #define MITKITKFILEIO_H #include "mitkAbstractFileIO.h" #include 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; protected: virtual std::vector 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> DoRead() override; private: ItkImageIO(const ItkImageIO &other); ItkImageIO *IOClone() const override; itk::ImageIOBase::Pointer m_ImageIO; std::vector 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 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 /* MITKITKFILEIO_H */ diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index d4666d22ed..55a4b86dc9 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,708 +1,724 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; + const char* const PROPERTY_KEY_UID = "org_mitk_uid"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName) { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType) { if ("GE4ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4"); } else if ("GE5ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5"); } else if ("Bruker2dseqImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq"); } } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } auto extensions = customReaderMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customReaderMimeType); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } auto extensions = customWriterMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customWriterMimeType); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const auto* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry) { std::stringstream stream; stream << timeGeometry->GetTimeBounds(0)[0]; const auto maxTimePoints = timeGeometry->CountTimeSteps(); for (TimeStepType pos = 0; pos < maxTimePoints; ++pos) { stream << " " << timeGeometry->GetTimeBounds(pos)[1]; } auto result = itk::MetaDataObject::New(); result->SetMetaDataObjectValue(stream.str()); return result.GetPointer(); }; std::vector ItkImageIO::DoRead() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } + // Handle UID + if (dictionary.HasKey(PROPERTY_KEY_UID)) + { + itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); + if (uidData.IsNotNull()) + { + mitk::UIDManipulator uidManipulator(image); + uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); + } + } + MITK_INFO << "...finished!"; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } + // Handle UID + itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID()); + ImageReadAccessor imageAccess(image); LocaleSwitch localeSwitch2("C"); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/test/mitkItkImageIOTest.cpp b/Modules/Core/test/mitkItkImageIOTest.cpp index 1c27efa749..1f608cc5f2 100644 --- a/Modules/Core/test/mitkItkImageIOTest.cpp +++ b/Modules/Core/test/mitkItkImageIOTest.cpp @@ -1,423 +1,427 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkException.h" #include #include #include #include "mitkIOUtil.h" #include "mitkITKImageImport.h" #include #include "itksys/SystemTools.hxx" #include #include #include #ifdef WIN32 #include "process.h" #else #include #endif class mitkItkImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkItkImageIOTestSuite); MITK_TEST(TestImageWriterJpg); MITK_TEST(TestImageWriterPng1); MITK_TEST(TestImageWriterPng2); MITK_TEST(TestImageWriterPng3); MITK_TEST(TestImageWriterSimple); MITK_TEST(TestWrite3DImageWithOnePlane); MITK_TEST(TestWrite3DImageWithTwoPlanes); MITK_TEST(TestWrite3DplusT_ArbitraryTG); MITK_TEST(TestWrite3DplusT_ProportionalTG); CPPUNIT_TEST_SUITE_END(); public: void setUp() override {} void tearDown() override {} void TestImageWriterJpg() { TestImageWriter("NrrdWritingTestImage.jpg"); } void TestImageWriterPng1() { TestImageWriter("Png2D-bw.png"); } void TestImageWriterPng2() { TestImageWriter("RenderingTestData/rgbImage.png"); } void TestImageWriterPng3() { TestImageWriter("RenderingTestData/rgbaImage.png"); } void TestWrite3DplusT_ArbitraryTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd"); } void TestWrite3DplusT_ProportionalTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_prop_time_geometry.nrrd"); } void TestImageWriterSimple() { // TODO } std::string AppendExtension(const std::string &filename, const char *extension) { std::string new_filename = filename; new_filename += extension; return new_filename; } bool CompareImageMetaData(mitk::Image::Pointer image, mitk::Image::Pointer reference, bool checkPixelType = true) { // switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed if (image->GetDimension() != reference->GetDimension()) { MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF(" << reference->GetDimension() << ")"; return false; } // pixel type if (checkPixelType && (image->GetPixelType() != reference->GetPixelType() && image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent())) { MITK_ERROR << "Pixeltype differs ( image=" << image->GetPixelType().GetPixelTypeAsString() << "[" << image->GetPixelType().GetBitsPerComponent() << "]" << " reference=" << reference->GetPixelType().GetPixelTypeAsString() << "[" << reference->GetPixelType().GetBitsPerComponent() << "]" << " )"; return false; } return true; } /* Test writing picture formats like *.bmp, *.png, *.tiff or *.jpg NOTE: Saving as picture format must ignore PixelType comparison - not all bits per components are supported (see specification of the format) */ void TestPictureWriting(mitk::Image *image, const std::string &filename, const std::string &extension) { const std::string fullFileName = AppendExtension(filename, extension.c_str()); mitk::Image::Pointer singleSliceImage = nullptr; if (image->GetDimension() == 3) { mitk::ExtractSliceFilter::Pointer extractFilter = mitk::ExtractSliceFilter::New(); extractFilter->SetInput(image); extractFilter->SetWorldGeometry(image->GetSlicedGeometry()->GetPlaneGeometry(0)); extractFilter->Update(); singleSliceImage = extractFilter->GetOutput(); // test 3D writing in format supporting only 2D mitk::IOUtil::Save(image, fullFileName); // test images unsigned int foundImagesCount = 0; // if the image only contains one sinlge slice the itkImageSeriesWriter won't add a number like // filename.XX.extension if (image->GetDimension(2) == 1) { std::stringstream series_filenames; series_filenames << filename << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } else // test the whole slice stack { for (unsigned int i = 0; i < image->GetDimension(2); i++) { std::stringstream series_filenames; series_filenames << filename << "." << i + 1 << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } } MITK_TEST_CONDITION(foundImagesCount == image->GetDimension(2), "All 2D-Slices of a 3D image were stored correctly."); } else if (image->GetDimension() == 2) { singleSliceImage = image; } // test 2D writing if (singleSliceImage.IsNotNull()) { try { mitk::IOUtil::Save(singleSliceImage, fullFileName); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(fullFileName.c_str()); MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was succesfully loaded again"); MITK_TEST_CONDITION_REQUIRED( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component remove(fullFileName.c_str()); } catch (itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "Exception during file writing for ." << extension << ": " << e.what()); } } } /** * test for writing NRRDs */ void TestNRRDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd"); tmpStream.close(); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was succesfully loaded again", compareImage.IsNotNull()); /*It would make sence to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE( "TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), mitk::eps, true)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); + remove(tmpFilePath.c_str()); } catch (...) { std::remove(tmpFilePath.c_str()); CPPUNIT_FAIL("Exception during NRRD file writing"); } } /** * test for writing MHDs */ void TestMHDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mhd"); tmpStream.close(); std::string tmpFilePathWithoutExt = tmpFilePath.substr(0, tmpFilePath.size() - 4); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was succesfully loaded again! ", compareImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE(".mhd file exists", itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".mhd").c_str())); CPPUNIT_ASSERT_MESSAGE(".raw or .zraw exists", itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".raw").c_str()) || itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".zraw").c_str())); /*It would make sence to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), 5e-4, true)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); + // delete remove(tmpFilePath.c_str()); remove((tmpFilePathWithoutExt + ".raw").c_str()); remove((tmpFilePathWithoutExt + ".zraw").c_str()); } catch (...) { CPPUNIT_FAIL("Exception during.mhd file writing"); } } /** * test for "ImageWriter". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ void TestImageWriter(std::string sourcefile) { sourcefile = GetTestDataFilePath(sourcefile); // load image CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(sourcefile.c_str())); mitk::Image::Pointer image = nullptr; try { image = mitk::IOUtil::Load(sourcefile); } catch (...) { CPPUNIT_FAIL("Exception during file loading:"); } CPPUNIT_ASSERT_MESSAGE("loaded image not nullptr", image.IsNotNull()); // write ITK .mhd image (2D and 3D only) if (image->GetDimension() <= 3) { TestMHDWriting(image); } // testing more component image writing as nrrd files TestNRRDWriting(image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX"); tmpStream.close(); TestPictureWriting(image, tmpFilePath, ".png"); TestPictureWriting(image, tmpFilePath, ".jpg"); TestPictureWriting(image, tmpFilePath, ".tiff"); TestPictureWriting(image, tmpFilePath, ".bmp"); // always end with this! } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithOnePlane() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 1; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")); } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithTwoPlanes() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 2; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkItkImageIO) diff --git a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.cpp similarity index 90% rename from Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp rename to Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.cpp index cee321423e..0b11df2dad 100644 --- a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp +++ b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.cpp @@ -1,410 +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 "mitkAlgorithmHelper.h" +#include "mitkMAPAlgorithmHelper.h" //itk #include // Mitk #include #include // MatchPoint #include #include #include #include #include namespace mitk { - MITKAlgorithmHelper::MITKAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm) + MAPAlgorithmHelper::MAPAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm) : m_AlgorithmBase(algorithm), m_Error(CheckError::none) { m_AllowImageCasting = true; } - bool MITKAlgorithmHelper::HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) + bool MAPAlgorithmHelper::HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) { using InternalDefault2DImageType = itk::Image; using InternalDefault3DImageType = itk::Image; using Alg2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; using Alg3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; using Alg2D3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; using Alg3D2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; return false; - }; + } - bool MITKAlgorithmHelper::HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) + bool MAPAlgorithmHelper::HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) { typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType; typedef const ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface PointSetRegInterface; return dynamic_cast(algorithm) != nullptr; - }; + } map::core::RegistrationBase::Pointer - MITKAlgorithmHelper:: + 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 MITKAlgorithmHelper."); + "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 MITKAlgorithmHelper."); + "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 (m_AlgorithmBase.GetPointer()); RegistrationAlg3D3DInterface* pRegAlgorithm3D3D = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pRegAlgorithm2D2D) { spResult = pRegAlgorithm2D2D->getRegistration(); } if (pRegAlgorithm3D3D) { spResult = pRegAlgorithm3D3D->getRegistration(); } return spResult; } mitk::MAPRegistrationWrapper::Pointer - MITKAlgorithmHelper:: + MAPAlgorithmHelper:: GetMITKRegistrationWrapper() const { map::core::RegistrationBase::Pointer spInternalResult = GetRegistration(); - mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(); - spResult->SetRegistration(spInternalResult); + mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(spInternalResult); return spResult; - }; + } static const mitk::Image* GetDataAsImage(const mitk::BaseData* data) { return dynamic_cast(data); - }; + } static const mitk::PointSet* GetDataAsPointSet(const mitk::BaseData* data) { return dynamic_cast(data); - }; + } bool - MITKAlgorithmHelper:: + 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 PointSetRegInterface; PointSetRegInterface* pPointSetInterface = dynamic_cast (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 MITKAlgorithmHelper::SetAllowImageCasting(bool allowCasting) + void MAPAlgorithmHelper::SetAllowImageCasting(bool allowCasting) { this->m_AllowImageCasting = allowCasting; - }; + } - bool MITKAlgorithmHelper::GetAllowImageCasting() const + bool MAPAlgorithmHelper::GetAllowImageCasting() const { return this->m_AllowImageCasting; - }; + } - void MITKAlgorithmHelper::SetData(const mitk::BaseData* moving, const mitk::BaseData* target) + 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 MITKAlgorithmHelper only supports images/point sets with same dimensionality."); + "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 PointSetRegInterface; PointSetRegInterface* pPointSetInterface = dynamic_cast (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 TOutImageType::Pointer MITKAlgorithmHelper::CastImage(const TInImageType* input) const + 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 - void MITKAlgorithmHelper::DoSetImages(const itk::Image* moving, + void MAPAlgorithmHelper::DoSetImages(const itk::Image* moving, const itk::Image* target) { typedef itk::Image MovingImageType; typedef itk::Image TargetImageType; typedef itk::Image InternalDefaultMovingImageType; typedef itk::Image InternalDefaultTargetImageType; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface ImageRegInterface; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface DefaultImageRegInterface; ImageRegInterface* pImageInterface = dynamic_cast(m_AlgorithmBase.GetPointer()); DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pImageInterface) { //just set directly and you are done /**@todo the duplication work arround 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. MITKAlgorithmHelper has to convert them into MatchPoint default images, but is not allowed. Please reconfigure helper."); + "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(target); typename InternalDefaultMovingImageType::Pointer spCastedMoving = CastImage(moving); pDefaultImageInterface->setTargetImage(spCastedTarget); pDefaultImageInterface->setMovingImage(spCastedMoving); } else { mapDefaultExceptionStaticMacro( << "Error, algorithm is not able to use the based images."); } } template - void MITKAlgorithmHelper::DoCheckImages(const itk::Image* /*moving*/, + void MAPAlgorithmHelper::DoCheckImages(const itk::Image* /*moving*/, const itk::Image* /*target*/) const { typedef itk::Image MovingImageType; typedef itk::Image TargetImageType; typedef itk::Image InternalDefaultMovingImageType; typedef itk::Image InternalDefaultTargetImageType; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface ImageRegInterface; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface DefaultImageRegInterface; ImageRegInterface* pImageInterface = dynamic_cast(m_AlgorithmBase.GetPointer()); DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast (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::InternalImageType, map::core::discrete::Elements<3>::InternalImageType, DummyRegIDPolicy> DummyRegType; DummyRegType::Pointer regAlg = DummyRegType::New(); - mitk::MITKAlgorithmHelper helper(regAlg); + 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); - mitk::MAPRegistrationWrapper::Pointer dummyReg = mitk::MAPRegistrationWrapper::New(); - dummyReg->SetRegistration(regAlg->getRegistration()); + auto dummyReg = mitk::MAPRegistrationWrapper::New(regAlg->getRegistration()); return dummyReg; } } diff --git a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.h b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.h similarity index 89% rename from Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.h rename to Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.h index 087c0b2425..70c42632f3 100644 --- a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.h +++ b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.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 mitkAlgorithmHelper_h -#define mitkAlgorithmHelper_h +#ifndef mitkMAPAlgorithmHelper_h +#define mitkMAPAlgorithmHelper_h //MatchPoint #include "mapRegistrationAlgorithmBase.h" #include "mapRegistrationBase.h" //MITK #include #include //MITK #include "MitkMatchPointRegistrationExports.h" #include "mitkMAPRegistrationWrapper.h" namespace mitk { /*! - \brief MITKAlgorithmHelper + \brief MAPAlgorithmHelper \remark Current implementation is not thread-save. Just use one Helper class per registration task. \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. */ - class MITKMATCHPOINTREGISTRATION_EXPORT MITKAlgorithmHelper + class MITKMATCHPOINTREGISTRATION_EXPORT MAPAlgorithmHelper { public: - MITKAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm); + MAPAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm); void SetData(const mitk::BaseData* moving, const mitk::BaseData* target); void SetAllowImageCasting(bool allowCasting); bool GetAllowImageCasting() const; static bool HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm); static bool HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm); struct CheckError { enum Type { none = 0, onlyByCasting = 1, wrongDimension = 2, unsupportedDataType = 3 }; }; bool CheckData(const mitk::BaseData* moving, const mitk::BaseData* target, CheckError::Type& error) const; map::core::RegistrationBase::Pointer GetRegistration() const; mitk::MAPRegistrationWrapper::Pointer GetMITKRegistrationWrapper() const; - ~MITKAlgorithmHelper() {} + ~MAPAlgorithmHelper() {} private: - MITKAlgorithmHelper& operator = (const MITKAlgorithmHelper&); - MITKAlgorithmHelper(const MITKAlgorithmHelper&); + MAPAlgorithmHelper& operator = (const MAPAlgorithmHelper&); + MAPAlgorithmHelper(const MAPAlgorithmHelper&); /**Internal helper that casts itk images from one pixel type into an other (used by DoSetImages if the images have the right dimension but wrong type and AllowImageCasting is activated)*/ template typename TOutImageType::Pointer CastImage(const TInImageType* input) const; /**Internal helper that is used by SetData if the data are images to set them properly.*/ template void DoSetImages(const itk::Image* moving, const itk::Image* target); /**Internal helper that is used by SetData if the data are images to check if the image types are supported by the algorithm.*/ template void DoCheckImages(const itk::Image* moving, const itk::Image* target) const; map::algorithm::RegistrationAlgorithmBase::Pointer m_AlgorithmBase; bool m_AllowImageCasting; mutable CheckError::Type m_Error; }; /**Small helper function that generates Identity transforms in 3D.*/ mitk::MAPRegistrationWrapper::Pointer MITKMATCHPOINTREGISTRATION_EXPORT GenerateIdentityRegistration3D(); } #endif diff --git a/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp index 6c18fa1dc2..5950e7ba52 100644 --- a/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp +++ b/Modules/MatchPointRegistration/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 #include #include -#include +#include 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))); 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::MITKAlgorithmHelper algHelper(m_Algorithm); + 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: " << *pos; } } }; double mitk::TimeFramesRegistrationHelper::GetProgress() const { return m_Progress; }; diff --git a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp index 8e809e044b..c5cf757609 100644 --- a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp +++ b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp @@ -1,280 +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 #include #include #include "mapRegistration.h" #include "mapRegistrationFileWriter.h" #include "mapRegistrationFileReader.h" #include "mapLazyFileFieldKernelLoader.h" #include #include #include #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::Execute(obj, data)) { return true; } return DimHelperSub::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 { 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 * 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::Execute(obj, data)) { return true; } return DimHelper::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 class CanWrite { public: static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& = "") { bool result = false; result = dynamic_cast *>(obj->GetRegistration()) != nullptr; return result; } }; /** Functor that writes the registration to a file if it has the right dimensionality. */ template class WriteReg { public: static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data) { const map::core::Registration* pReg = dynamic_cast*>(obj->GetRegistration()); if (pReg == nullptr) { return false; } typedef map::io::RegistrationFileWriter 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(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(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 MAPRegistrationWrapperIO::DoRead() { std::vector 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); - mitk::MAPRegistrationWrapper::Pointer spRegWrapper = mitk::MAPRegistrationWrapper::New(); - spRegWrapper->SetRegistration(spReg); + 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/files.cmake b/Modules/MatchPointRegistration/files.cmake index 8546d7de97..1b9c8ff7e1 100644 --- a/Modules/MatchPointRegistration/files.cmake +++ b/Modules/MatchPointRegistration/files.cmake @@ -1,65 +1,65 @@ set(CPP_FILES mitkMAPRegistrationWrapper.cpp mitkMAPRegistrationWrapperObjectFactory.cpp mitkRegEvaluationObjectFactory.cpp mitkRegEvaluationObject.cpp Helper/mitkUIDHelper.cpp - Helper/mitkAlgorithmHelper.cpp + Helper/mitkMAPAlgorithmHelper.cpp Helper/mitkMaskedAlgorithmHelper.cpp Helper/mitkRegistrationHelper.cpp Helper/mitkImageMappingHelper.cpp Helper/mitkPointSetMappingHelper.cpp Helper/mitkResultNodeGenerationHelper.cpp Helper/mitkTimeFramesRegistrationHelper.cpp Rendering/mitkRegistrationWrapperMapper2D.cpp Rendering/mitkRegistrationWrapperMapper3D.cpp Rendering/mitkRegistrationWrapperMapperBase.cpp Rendering/mitkRegEvaluationMapper2D.cpp Rendering/mitkRegVisStyleProperty.cpp Rendering/mitkRegVisDirectionProperty.cpp Rendering/mitkRegVisColorStyleProperty.cpp Rendering/mitkRegVisPropertyTags.cpp Rendering/mitkRegVisHelper.cpp Rendering/mitkRegEvalStyleProperty.cpp Rendering/mitkRegEvalWipeStyleProperty.cpp ) set(H_FILES mitkMatchPointPropertyTags.h mitkMAPRegistrationWrapper.h mitkMAPRegistrationWrapperObjectFactory.h mitkRegEvaluationObjectFactory.h mitkRegEvaluationObject.h algorithms/mitkMultiModalAffineDefaultRegistrationAlgorithm.h algorithms/mitkMultiModalRigidDefaultRegistrationAlgorithm.h algorithms/mitkMultiModalTransDefaultRegistrationAlgorithm.h algorithms/mitkFastSymmetricForcesDemonsMultiResDefaultRegistrationAlgorithm.h algorithms/mitkLevelSetMotionMultiResDefaultRegistrationAlgorithm.h algorithms/mitkRigidClosedFormPointsDefaultRegistrationAlgorithm.h algorithms/mitkRigidICPDefaultRegistrationAlgorithm.h Helper/mitkUIDHelper.h - Helper/mitkAlgorithmHelper.h + Helper/mitkMAPAlgorithmHelper.h Helper/mitkMaskedAlgorithmHelper.h Helper/mitkRegistrationHelper.h Helper/mitkImageMappingHelper.h Helper/mitkPointSetMappingHelper.h Helper/mitkResultNodeGenerationHelper.h Helper/mitkTimeFramesRegistrationHelper.h Rendering/mitkRegistrationWrapperMapper2D.h Rendering/mitkRegistrationWrapperMapper3D.h Rendering/mitkRegistrationWrapperMapperBase.h Rendering/mitkRegVisStyleProperty.h Rendering/mitkRegVisDirectionProperty.h Rendering/mitkRegVisColorStyleProperty.h Rendering/mitkRegVisPropertyTags.h Rendering/mitkRegVisHelper.h Rendering/mitkRegEvaluationMapper2D.h Rendering/mitkRegEvalStyleProperty.h Rendering/mitkRegEvalWipeStyleProperty.h ) set(TPP_FILES ) set(MOC_H_FILES ) diff --git a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp index 616f02f66b..2ad231a33d 100644 --- a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp +++ b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp @@ -1,137 +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 "mitkMAPRegistrationWrapper.h" #include +#include -mitk::MAPRegistrationWrapper::MAPRegistrationWrapper() +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; } 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)."; } return m_spRegistration->hasLimitedTargetRepresentation(); } bool mitk::MAPRegistrationWrapper::HasLimitedMovingRepresentation() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot determin 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::SetRegistration(map::core::RegistrationBase* pReg) -{ - m_spRegistration = pReg; -} - 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<Print(os,indent.GetNextIndent()); typedef map::core::Registration<3,3> CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); os<getDirectMapping().Print(os,indent.GetNextIndent()); os<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/MatchPointRegistration/mitkMAPRegistrationWrapper.h b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h index ece571bede..734392a845 100644 --- a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h +++ b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h @@ -1,268 +1,270 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 #include //MatchPoint #include #include #include #include //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 ); - itkNewMacro( Self ); + mitkNewMacro1Param( Self, ::map::core::RegistrationBase*); + + Identifiable::UIDType GetUID() 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 bool MapPoint(const ::itk::Point& inPoint, ::itk::Point& outPoint) const { typedef typename ::map::core::continuous::Elements::PointType MAPMovingPointType; typedef typename ::map::core::continuous::Elements::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 CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(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 bool MapPointInverse(const ::itk::Point & inPoint, ::itk::Point & outPoint) const { typedef typename ::map::core::continuous::Elements::PointType MAPMovingPointType; typedef typename ::map::core::continuous::Elements::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 CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(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. @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. @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; - void SetRegistration(::map::core::RegistrationBase* pReg); - protected: void PrintSelf (std::ostream &os, itk::Indent indent) const override; - MAPRegistrationWrapper(); + 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/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp index 885e7acfdb..76197de9b9 100644 --- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp +++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp @@ -1,201 +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 +#include #include #include #include #include #include #include // Qt #include // MatchPoint #include #include #include #include #include const mitk::Image *QmitkRegistrationJob::GetTargetDataAsImage() const { return dynamic_cast(m_spTargetData.GetPointer()); -}; +} const mitk::Image *QmitkRegistrationJob::GetMovingDataAsImage() const { return dynamic_cast(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(&event); const map::events::AlgorithmIterationEvent *pIterationEvent = dynamic_cast(&event); const map::events::AlgorithmWrapperEvent *pWrapEvent = dynamic_cast(&event); const map::events::AlgorithmResolutionLevelEvent *pLevelEvent = dynamic_cast(&event); const map::events::InitializingAlgorithmEvent *pInitEvent = dynamic_cast(&event); const map::events::StartingAlgorithmEvent *pStartEvent = dynamic_cast(&event); const map::events::StoppingAlgorithmEvent *pStoppingEvent = dynamic_cast(&event); const map::events::StoppedAlgorithmEvent *pStoppedEvent = dynamic_cast(&event); const map::events::FinalizingAlgorithmEvent *pFinalizingEvent = dynamic_cast(&event); const map::events::FinalizedAlgorithmEvent *pFinalizedEvent = dynamic_cast(&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(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(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::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::MITKAlgorithmHelper helper(m_spLoadedAlgorithm); + 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 { - mitk::MAPRegistrationWrapper::Pointer spRegWrapper = mitk::MAPRegistrationWrapper::New(); - spRegWrapper->SetRegistration(m_spResultRegistration); + 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 Finished(); -}; +} diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp index 41f5f03a67..e5bc2b557d 100644 --- a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp +++ b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp @@ -1,234 +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. ============================================================================*/ #include #include #include #include #include #include #include #include std::string pathToImage; class mitkLabelSetImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLabelSetImageIOTestSuite); MITK_TEST(TestReadWrite3DLabelSetImage); MITK_TEST(TestReadWrite3DplusTLabelSetImage); MITK_TEST(TestReadWrite3DplusTLabelSetImageWithArbitraryGeometry); MITK_TEST(TestReadWriteProperties); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer regularImage; mitk::LabelSetImage::Pointer multilabelImage; public: void setUp() override { regularImage = mitk::Image::New(); } void tearDown() override { regularImage = nullptr; multilabelImage = nullptr; } void TestReadWrite3DLabelSetImage() { unsigned int dimensions[3] = {30, 20, 10}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(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); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3D.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", multilabelImage->GetUID(), loadedImage->GetUID()); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWrite3DplusTLabelSetImage() { unsigned int dimensions[4] = {30, 20, 15, 10}; regularImage->Initialize(mitk::MakeScalarPixelType(), 4, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(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); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3DplusT.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); CPPUNIT_ASSERT_MESSAGE("Error reading time geometry of label set image", mitk::Equal(*(multilabelImage->GetTimeGeometry()), *(loadedImage->GetTimeGeometry()), 0.000000001, true)); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWrite3DplusTLabelSetImageWithArbitraryGeometry() { unsigned int dimensions[4] = { 30, 20, 10, 4 }; regularImage->Initialize(mitk::MakeScalarPixelType(), 4, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(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); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); auto geometry = multilabelImage->GetGeometry()->Clone(); auto refTimeGeometry = mitk::ArbitraryTimeGeometry::New(); refTimeGeometry->AppendNewTimeStep(geometry, 0., 0.5); refTimeGeometry->AppendNewTimeStep(geometry, 0.5, 1.); refTimeGeometry->AppendNewTimeStep(geometry, 1., 2.); refTimeGeometry->AppendNewTimeStep(geometry, 2., 5.5); multilabelImage->SetTimeGeometry(refTimeGeometry); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3DplusTWithArbitraryTimeGeometry.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); CPPUNIT_ASSERT_MESSAGE("Error reading time geometry of label set image", mitk::Equal(*refTimeGeometry, *(loadedImage->GetTimeGeometry()), 0.000000001, true)); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWriteProperties() { unsigned int dimensions[3] = { 30, 20, 10 }; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); newlayer->AddLabel(label0); multilabelImage->AddLayer(newlayer); auto propPersistenceInfo = mitk::PropertyPersistenceInfo::New(); propPersistenceInfo->SetNameAndKey("my.cool.test.property", "my_cool_test_property"); mitk::CoreServicePointer propPersService(mitk::CoreServices::GetPropertyPersistence()); propPersService->AddInfo(propPersistenceInfo); multilabelImage->SetProperty("my.cool.test.property", mitk::StringProperty::New("test_content")); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetPropertiesTestImage.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); auto loadedProp = loadedImage->GetProperty("my.cool.test.property"); CPPUNIT_ASSERT_MESSAGE("Error reading properties of label set image", loadedProp.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading properties of label set image", loadedProp->GetValueAsString() == "test_content"); itksys::SystemTools::RemoveFile(pathToImage); } }; MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImageIO) diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index 9466f4a70e..0e041818c9 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,636 +1,652 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include #include #include #include #include +#include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" namespace mitk { const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; + const char* const PROPERTY_KEY_UID = "org_mitk_uid"; LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if (inputVector.IsNull()) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int *const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::unique_ptr document; document.reset(new TiXmlDocument()); auto *decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... document->LinkEndChild(decl); TiXmlElement *labelElem = mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(iter->second); document->LinkEndChild(labelElem); TiXmlPrinter printer; printer.SetIndent(""); printer.SetLineBreak(""); document->Accept(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str()); ++iter; ++count; } } // end label set specific meta data // Handle time geometry const auto* arbitraryTG = dynamic_cast(input->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList(); for (const auto& property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), key, value); } + // Handle UID + itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID()); + ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { TiXmlDocument doc; sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str()); TiXmlElement *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string& key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); output->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey = false; for (const auto& defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } + // Handle UID + if (dictionary.HasKey(PROPERTY_KEY_UID)) + { + itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); + if (uidData.IsNotNull()) + { + mitk::UIDManipulator uidManipulator(output); + uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); + } + } + MITK_INFO << "...finished!"; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } void LabelSetImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); this->m_DefaultMetaDataKeys.push_back("label_"); this->m_DefaultMetaDataKeys.push_back("layer_"); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h index 4b079dc5e4..2c78d6d26c 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h @@ -1,68 +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 __mitkLabelSetImageIO_h #define __mitkLabelSetImageIO_h #include #include namespace mitk { /** - * Writes a LabelSetImage to a file + * 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 LabelSetImageIO : public mitk::AbstractFileIO { public: typedef mitk::LabelSetImage InputType; LabelSetImageIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; // -------------- LabelSetImageIO specific functions ------------- int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); 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 */ std::vector> DoRead() override; // Fills the m_DefaultMetaDataKeys vector with default values virtual void InitializeDefaultMetaDataKeys(); private: LabelSetImageIO *IOClone() const override; std::vector m_DefaultMetaDataKeys; }; } // end of namespace mitk #endif // __mitkLabelSetImageIO_h diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp index d4f9954913..a6b5c851af 100644 --- a/Modules/SceneSerialization/src/mitkSceneIO.cpp +++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp @@ -1,577 +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 #include #include #include #include #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 #include #include #include #include #include #include #include "itksys/SystemTools.hxx" 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)"; 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; } } } 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>( this, &SceneIO::OnUnzipError); unzipper.EOk += Poco::Delegate>( this, &SceneIO::OnUnzipOk); unzipper.decompressAllFiles(); unzipper.EError -= Poco::Delegate>( this, &SceneIO::OnUnzipError); unzipper.EOk -= Poco::Delegate>( 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 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 TiXmlDocument document(indexfilename); if (!document.LoadFile()) { MITK_ERROR << "Could not open/read/parse " << workingDir << mitk::IOUtil::GetDirectorySeparator() << "index.xml\nTinyXML reports: " << document.ErrorDesc() << 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 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 TiXmlDocument document; auto *decl = new TiXmlDeclaration( "1.0", "UTF-8", ""); // TODO what to write here? encoding? standalone would mean that we provide a DTD somewhere... document.LinkEndChild(decl); auto *version = new TiXmlElement("Version"); version->SetAttribute("Writer", __FILE__); version->SetAttribute("Revision", "$Revision: 17055 $"); version->SetAttribute("FileVersion", 1); document.LinkEndChild(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 UIDMapType; typedef std::map> 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 = new TiXmlElement("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 = new TiXmlElement("source"); uidElement->SetAttribute("UID", sourceUIDIter->c_str()); nodeElement->LinkEndChild(uidElement); } } // store basedata if (BaseData *data = node->GetData()) { // std::string filenameHint( node->GetName() ); bool error(false); TiXmlElement *dataElement(SaveBaseData(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()) { TiXmlElement *baseDataPropertiesElement( SavePropertyList(propertyList, filenameHint + "-data")); // returns a reference to a file dataElement->LinkEndChild(baseDataPropertiesElement); } nodeElement->LinkEndChild(dataElement); } // store all renderwindow specific propertylists mitk::DataNode::PropertyListKeyNames propertyListKeys = node->GetPropertyListNames(); for (auto renderWindowName : propertyListKeys) { PropertyList *propertyList = node->GetPropertyList(renderWindowName); if (propertyList && !propertyList->IsEmpty()) { TiXmlElement *renderWindowPropertiesElement( SavePropertyList(propertyList, filenameHint + "-" + renderWindowName)); // returns a reference to a file renderWindowPropertiesElement->SetAttribute("renderwindow", renderWindowName); nodeElement->LinkEndChild(renderWindowPropertiesElement); } } // don't forget the renderwindow independent list PropertyList *propertyList = node->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { TiXmlElement *propertiesElement( SavePropertyList(propertyList, filenameHint + "-node")); // returns a reference to a file nodeElement->LinkEndChild(propertiesElement); } document.LinkEndChild(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 ); if (!document.SaveFile(defaultLocale_WorkingDirectory + Poco::Path::separator() + "index.xml")) { MITK_ERROR << "Could not write scene to " << defaultLocale_WorkingDirectory << Poco::Path::separator() << "index.xml" << "\nTinyXML reports '" << document.ErrorDesc() << "'"; 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; } } TiXmlElement *mitk::SceneIO::SaveBaseData(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 = new TiXmlElement("data"); element->SetAttribute("type", data->GetNameOfClass()); // construct name of serializer class std::string serializername(data->GetNameOfClass()); serializername += "Serializer"; std::list 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(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); error = false; } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } + element->SetAttribute("UID", data->GetUID()); return element; } TiXmlElement *mitk::SceneIO::SavePropertyList(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 = new TiXmlElement("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); 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 &info) { ++m_UnzipErrors; MITK_ERROR << "Error while unzipping: " << info.second; } void mitk::SceneIO::OnUnzipOk(const void * /*pSender*/, std::pair & /*info*/) { // MITK_INFO << "Unzipped ok: " << info.second.toString(); } diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp index adc64156f3..5f349a4d22 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp @@ -1,421 +1,429 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 #include MITK_REGISTER_SERIALIZER(SceneReaderV1) namespace { typedef std::pair> 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(); } } bool mitk::SceneReaderV1::LoadScene(TiXmlDocument &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 // 1. if there is a 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 DataNodeVector; DataNodeVector DataNodes; unsigned int listSize = 0; for (TiXmlElement *element = document.FirstChildElement("node"); element != nullptr; element = element->NextSiblingElement("node")) { ++listSize; } ProgressBar::GetInstance()->AddStepsToDo(listSize * 2); for (TiXmlElement *element = document.FirstChildElement("node"); element != nullptr; element = element->NextSiblingElement("node")) { DataNodes.push_back(LoadBaseDataFromDataTag(element->FirstChildElement("data"), workingDirectory, error)); ProgressBar::GetInstance()->Progress(); } // iterate all nodes // first level nodes should be elements auto nit = DataNodes.begin(); for (TiXmlElement *element = document.FirstChildElement("node"); element != nullptr || nit != DataNodes.end(); element = element->NextSiblingElement("node"), ++nit) { mitk::DataNode::Pointer node = *nit; // in case dataXmlElement is valid test whether it containts the "properties" child tag // and process further if and only if yes TiXmlElement *dataXmlElement = element->FirstChildElement("data"); if (dataXmlElement && dataXmlElement->FirstChildElement("properties")) { TiXmlElement *baseDataElement = dataXmlElement->FirstChildElement("properties"); if (node->GetData()) { DecorateBaseDataWithProperties(node->GetData(), baseDataElement, workingDirectory); } else { MITK_WARN << "BaseData properties stored in scene file, but BaseData could not be read" << std::endl; } } // 2. 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; } // 3. if there are 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())); // 4. if there are elements, remember parent objects for (TiXmlElement *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 // 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(TiXmlElement *dataElement, const std::string &workingDirectory, bool &error) { DataNode::Pointer node; if (dataElement) { const char *filename = dataElement->Attribute("file"); if (filename && strlen(filename) != 0) { try { std::vector baseData = IOUtil::Load(workingDirectory + Poco::Path::separator() + filename); if (baseData.size() > 1) { MITK_WARN << "Discarding multiple base data results from " << filename << " except the first one."; } node = DataNode::New(); node->SetData(baseData.front()); } 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; } } + + const char* dataUID = dataElement->Attribute("UID"); + if (!error && nullptr != dataUID && 0 != strlen(dataUID)) + { + UIDManipulator manip(node->GetData()); + manip.SetUID(dataUID); + } } // in case there was no 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(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, TiXmlElement *nodeElement, const std::string &workingDirectory) { assert(node); assert(nodeElement); bool error(false); for (TiXmlElement *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; } bool mitk::SceneReaderV1::DecorateBaseDataWithProperties(BaseData::Pointer data, TiXmlElement *baseDataNodeElem, const std::string &workingDir) { // check given variables, initialize error variable assert(baseDataNodeElem); bool error(false); // get the file name stored in the tag const char *baseDataPropertyFile(baseDataNodeElem->Attribute("file")); // check if the filename was found if (baseDataPropertyFile) { // PropertyList::Pointer dataPropList = data->GetPropertyList(); PropertyListDeserializer::Pointer propertyDeserializer = PropertyListDeserializer::New(); // initialize the property reader propertyDeserializer->SetFilename(workingDir + Poco::Path::separator() + baseDataPropertyFile); bool ioSuccess = propertyDeserializer->Deserialize(); error = !ioSuccess; // get the output PropertyList::Pointer inProperties = propertyDeserializer->GetOutput(); // store the read-in properties to the given node or throw error otherwise if (inProperties.IsNotNull()) { data->SetPropertyList(inProperties); } else { MITK_ERROR << "The property deserializer did not return a (valid) property list."; error = true; } } else { MITK_ERROR << "Function DecorateBaseDataWithProperties(...) called with false TiXmlElement. \n \t ->Given element " "does not contain a 'file' attribute. \n"; error = true; } return !error; } diff --git a/Modules/SceneSerialization/test/mitkBaseDataCompare.cpp b/Modules/SceneSerialization/test/mitkBaseDataCompare.cpp index 9167363315..fdfe69d408 100644 --- a/Modules/SceneSerialization/test/mitkBaseDataCompare.cpp +++ b/Modules/SceneSerialization/test/mitkBaseDataCompare.cpp @@ -1,99 +1,103 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkGeometryData.h" #include "mitkImage.h" #include "mitkPointSet.h" #include "mitkSurface.h" // this include after all specific type includes! (for mitk::Equal) #include "mitkBaseDataCompare.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceProperties.h" bool mitk::BaseDataCompare::AreEqual(const BaseData *left, const BaseData *right, ScalarType eps, bool verbose) { // Do basic tests that are valid for all types here. // Let specializations only implement a meaningful // comparison of two non-null objects of same type if (left == nullptr && right == nullptr) return true; if (AreSameClasses(left, right, verbose)) { + if (left->GetUID() != right->GetUID()) + { + return false; + } return InternalAreEqual(*left, *right, eps, verbose); } return false; } bool mitk::BaseDataCompare::AreSameClasses(const BaseData *left, const BaseData *right, bool verbose) { if (left == nullptr && right == nullptr) return true; if (left == nullptr && right != nullptr) { if (verbose) MITK_WARN << "Left data is nullptr, right data is not (type " << right->GetNameOfClass() << ")"; return false; } if (left != nullptr && right == nullptr) { if (verbose) MITK_WARN << "Right data is nullptr, left data is not (type " << left->GetNameOfClass() << ")"; return false; } // two real BaseData objects, need to really compare if (left->GetNameOfClass() != right->GetNameOfClass()) { if (verbose) MITK_WARN << "Mismatch: Left data is '" << left->GetNameOfClass() << "', " << "right data is '" << right->GetNameOfClass() << "'"; return false; } return true; } void mitk::BaseDataCompare::RegisterCoreEquals() { static bool comparatorsCreated = false; if (!comparatorsCreated) { static BaseDataCompareT imageEqual; us::ServiceProperties imageProperties; imageProperties["basedata"] = std::string(Image::GetStaticNameOfClass()); us::GetModuleContext()->RegisterService(&imageEqual, imageProperties); static BaseDataCompareTNonConst surfaceEqual; us::ServiceProperties surfaceProperties; surfaceProperties["basedata"] = std::string(Surface::GetStaticNameOfClass()); us::GetModuleContext()->RegisterService(&surfaceEqual, surfaceProperties); static BaseDataCompareT pointsetEqual; us::ServiceProperties pointsetProperties; pointsetProperties["basedata"] = std::string(PointSet::GetStaticNameOfClass()); us::GetModuleContext()->RegisterService(&pointsetEqual, pointsetProperties); static BaseDataCompareT geometryDataEqual; us::ServiceProperties geometryDataProperties; geometryDataProperties["basedata"] = std::string(GeometryData::GetStaticNameOfClass()); us::GetModuleContext()->RegisterService(&geometryDataEqual, geometryDataProperties); comparatorsCreated = true; } } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp b/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp index 990ecb068b..637b5db3b3 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp @@ -1,867 +1,867 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "org_mitk_gui_qt_matchpoint_algorithmcontrol_Activator.h" // Blueberry #include #include #include #include // Mitk #include #include #include #include #include -#include +#include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPoint.h" #include #include // Qt #include #include #include #include #include // MatchPoint #include #include #include #include #include #include #include #include #include const std::string QmitkMatchPoint::VIEW_ID = "org.mitk.views.matchpoint.algorithm.control"; QmitkMatchPoint::QmitkMatchPoint() : m_Parent(nullptr), m_LoadedDLLHandle(nullptr), m_LoadedAlgorithm(nullptr) { m_CanLoadAlgorithm = false; m_ValidInputs = false; m_Working = false; m_spSelectedTargetData = nullptr; m_spSelectedMovingData = nullptr; m_spSelectedTargetMaskData = nullptr; m_spSelectedMovingMaskData = nullptr; } QmitkMatchPoint::~QmitkMatchPoint() { // remove selection service berry::ISelectionService* s = this->GetSite()->GetWorkbenchWindow()->GetSelectionService(); if (s) { s->RemoveSelectionListener(m_AlgorithmSelectionListener.data()); } } void QmitkMatchPoint::SetFocus() { } void QmitkMatchPoint::CreateConnections() { connect(m_Controls.targetNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); connect(m_Controls.movingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); connect(m_Controls.targetMaskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); connect(m_Controls.movingMaskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); // ------ // Tab 1 - Shared library loading interface // ------ connect(m_Controls.m_pbLoadSelected, SIGNAL(clicked()), this, SLOT(OnLoadAlgorithmButtonPushed())); // ----- // Tab 2 - Execution // ----- connect(m_Controls.m_pbStartReg, SIGNAL(clicked()), this, SLOT(OnStartRegBtnPushed())); connect(m_Controls.m_pbStopReg, SIGNAL(clicked()), this, SLOT(OnStopRegBtnPushed())); connect(m_Controls.m_pbSaveLog, SIGNAL(clicked()), this, SLOT(OnSaveLogBtnPushed())); } const map::deployment::DLLInfo* QmitkMatchPoint::GetSelectedAlgorithmDLL() const { return m_SelectedAlgorithmInfo; } void QmitkMatchPoint::OnSelectedAlgorithmChanged() { std::stringstream descriptionString; ::map::deployment::DLLInfo::ConstPointer currentItemInfo = GetSelectedAlgorithmDLL(); if (!currentItemInfo) { Error(QStringLiteral("No valid algorithm is selected. ABORTING.")); return; } m_Controls.m_teAlgorithmDetails->updateInfo(currentItemInfo); m_Controls.m_lbSelectedAlgorithm->setText(QString::fromStdString( currentItemInfo->getAlgorithmUID().getName())); // enable loading m_CanLoadAlgorithm = true; this->AdaptFolderGUIElements(); } void QmitkMatchPoint::OnLoadAlgorithmButtonPushed() { map::deployment::DLLInfo::ConstPointer dllInfo = GetSelectedAlgorithmDLL(); if (!dllInfo) { Error(QStringLiteral("No valid algorithm is selected. Cannot load algorithm. ABORTING.")); return; } ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( dllInfo->getLibraryFilePath()); ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); if (tempAlgorithm.IsNull()) { Error(QStringLiteral("Error. Cannot load selected algorithm.")); return; } this->m_LoadedAlgorithm = tempAlgorithm; this->m_LoadedDLLHandle = tempDLLHandle; this->m_Controls.m_AlgoConfigurator->setAlgorithm(m_LoadedAlgorithm); typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface; const MaskRegInterface* pMaskReg = dynamic_cast (m_LoadedAlgorithm.GetPointer()); if (!pMaskReg) { m_spSelectedTargetMaskData = nullptr; m_spSelectedTargetMaskNode = nullptr; m_spSelectedMovingMaskData = nullptr; m_spSelectedMovingMaskNode = nullptr; m_Controls.targetMaskNodeSelector->SetCurrentSelection(QmitkAbstractNodeSelectionWidget::NodeList()); m_Controls.movingMaskNodeSelector->SetCurrentSelection(QmitkAbstractNodeSelectionWidget::NodeList()); } this->AdaptFolderGUIElements(); this->ConfigureNodeSelectors(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); this->m_Controls.m_tabs->setCurrentIndex(1); this->m_Controls.m_teLog->clear(); } void QmitkMatchPoint::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QStringLiteral("") + msg + QStringLiteral("")); } void QmitkMatchPoint::AdaptFolderGUIElements() { m_Controls.m_pbLoadSelected->setEnabled(m_CanLoadAlgorithm); } void QmitkMatchPoint::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; m_Controls.m_tabs->setCurrentIndex(0); m_Controls.movingNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.movingNodeSelector->SetSelectionIsOptional(false); m_Controls.targetNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.targetNodeSelector->SetSelectionIsOptional(false); m_Controls.movingMaskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.movingMaskNodeSelector->SetSelectionIsOptional(true); m_Controls.targetMaskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.targetMaskNodeSelector->SetSelectionIsOptional(true); m_AlgorithmSelectionListener.reset(new berry::SelectionChangedAdapter(this, &QmitkMatchPoint::OnAlgorithmSelectionChanged)); // register selection listener GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddSelectionListener( m_AlgorithmSelectionListener.data()); this->CreateConnections(); this->AdaptFolderGUIElements(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureRegistrationControls(); this->ConfigureNodeSelectors(); berry::ISelection::ConstPointer selection = GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.matchpoint.algorithm.browser"); this->UpdateAlgorithmSelection(selection); } 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(); } bool QmitkMatchPoint::CheckInputs() { if (m_LoadedAlgorithm.IsNull()) { m_spSelectedMovingNode = nullptr; m_spSelectedMovingData = nullptr; m_spSelectedTargetNode = nullptr; m_spSelectedTargetData = nullptr; m_spSelectedMovingMaskNode = nullptr; m_spSelectedMovingMaskData = nullptr; m_spSelectedTargetMaskNode = nullptr; m_spSelectedTargetMaskData = nullptr; } else { if (m_Controls.movingNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedMovingNode = nullptr; m_spSelectedMovingData = nullptr; } else { m_spSelectedMovingNode = m_Controls.movingNodeSelector->GetSelectedNode(); m_spSelectedMovingData = m_spSelectedMovingNode->GetData(); auto movingImage = dynamic_cast(m_spSelectedMovingNode->GetData()); if (movingImage && movingImage->GetDimension() - 1 == m_LoadedAlgorithm->getMovingDimensions() && movingImage->GetTimeSteps() > 1) { m_spSelectedMovingData = ExtractFirstFrame(movingImage).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected moving image has multiple time steps. First time step is used as moving image.")); } } if (m_Controls.targetNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedTargetNode = nullptr; m_spSelectedTargetData = nullptr; } else { m_spSelectedTargetNode = m_Controls.targetNodeSelector->GetSelectedNode(); m_spSelectedTargetData = m_spSelectedTargetNode->GetData(); auto targetImage = dynamic_cast(m_spSelectedTargetNode->GetData()); if (targetImage && targetImage->GetDimension() - 1 == m_LoadedAlgorithm->getTargetDimensions() && targetImage->GetTimeSteps() > 1) { m_spSelectedTargetData = ExtractFirstFrame(targetImage).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected target image has multiple time steps. First time step is used as target image.")); } } if (m_Controls.movingMaskNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedMovingMaskNode = nullptr; m_spSelectedMovingMaskData = nullptr; } else { m_spSelectedMovingMaskNode = m_Controls.movingMaskNodeSelector->GetSelectedNode(); m_spSelectedMovingMaskData = dynamic_cast(m_spSelectedMovingMaskNode->GetData()); if (m_spSelectedMovingMaskData->GetDimension() - 1 == m_LoadedAlgorithm->getMovingDimensions() && m_spSelectedMovingMaskData->GetTimeSteps() > 1) { m_spSelectedMovingMaskData = ExtractFirstFrame(m_spSelectedMovingMaskData).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected moving mask has multiple time steps. First time step is used as moving mask.")); } } if (m_Controls.targetMaskNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedTargetMaskNode = nullptr; m_spSelectedTargetMaskData = nullptr; } else { m_spSelectedTargetMaskNode = m_Controls.targetMaskNodeSelector->GetSelectedNode(); m_spSelectedTargetMaskData = dynamic_cast(m_spSelectedTargetMaskNode->GetData()); if (m_spSelectedTargetMaskData->GetDimension() - 1 == m_LoadedAlgorithm->getTargetDimensions() && m_spSelectedTargetMaskData->GetTimeSteps() > 1) { m_spSelectedTargetMaskData = ExtractFirstFrame(m_spSelectedTargetMaskData).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected target mask has multiple time steps. First time step is used as target mask.")); } } } m_ValidInputs = m_spSelectedMovingData.IsNotNull() && m_spSelectedTargetData.IsNotNull(); return m_ValidInputs; } std::string QmitkMatchPoint::GetInputNodeDisplayName(const mitk::DataNode* node) const { std::string result = "UNDEFINED/nullptr"; if (node) { result = node->GetName(); const mitk::PointSet* pointSet = dynamic_cast(node->GetData()); if (pointSet) { mitk::DataStorage::SetOfObjects::ConstPointer sources = this->GetDataStorage()->GetSources(node); if (sources.IsNotNull() && sources->Size() > 0) { result = result + " (" + sources->GetElement(0)->GetName() + ")"; } } } return result; } mitk::DataStorage::SetOfObjects::Pointer QmitkMatchPoint::GetRegNodes() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::const_iterator pos = nodes->begin(); pos != nodes->end(); ++pos) { if (mitk::MITKRegistrationHelper::IsRegNode(*pos)) { result->push_back(*pos); } } return result; } std::string QmitkMatchPoint::GetDefaultRegJobName() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetRegNodes().GetPointer(); mitk::DataStorage::SetOfObjects::ElementIdentifier estimatedIndex = nodes->Size(); bool isUnique = false; std::string result = "Unnamed Reg"; while (!isUnique) { ++estimatedIndex; result = "Reg #" +::map::core::convert::toStr(estimatedIndex); isUnique = this->GetDataStorage()->GetNamedNode(result) == nullptr; } return result; } void QmitkMatchPoint::ConfigureRegistrationControls() { m_Controls.m_tabSelection->setEnabled(!m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); m_Controls.groupMasks->setEnabled(!m_Working); m_Controls.m_pbStartReg->setEnabled(false); m_Controls.m_pbStopReg->setEnabled(false); m_Controls.m_pbStopReg->setVisible(false); if (m_LoadedAlgorithm.IsNotNull()) { m_Controls.m_tabSettings->setEnabled(!m_Working); m_Controls.m_tabExecution->setEnabled(true); m_Controls.m_pbStartReg->setEnabled(m_ValidInputs && !m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); m_Controls.m_checkMapEntity->setEnabled(!m_Working); m_Controls.targetNodeSelector->setEnabled(!m_Working); m_Controls.movingNodeSelector->setEnabled(!m_Working); m_Controls.targetMaskNodeSelector->setEnabled(!m_Working); m_Controls.movingMaskNodeSelector->setEnabled(!m_Working); const IStoppableAlgorithm* pIterativ = dynamic_cast (m_LoadedAlgorithm.GetPointer()); if (pIterativ) { m_Controls.m_pbStopReg->setVisible(pIterativ->isStoppable()); } typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface; const MaskRegInterface* pMaskReg = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.groupMasks->setVisible(pMaskReg != nullptr); //if the stop button is set to visible and the algorithm is working -> //then the algorithm is stoppable, thus enable the button. m_Controls.m_pbStopReg->setEnabled(m_Controls.m_pbStopReg->isVisible() && m_Working); this->m_Controls.m_lbLoadedAlgorithmName->setText( QString::fromStdString(m_LoadedAlgorithm->getUID()->toStr())); } else { m_Controls.m_tabSettings->setEnabled(false); m_Controls.m_tabExecution->setEnabled(false); this->m_Controls.m_lbLoadedAlgorithmName->setText( QStringLiteral("no algorithm loaded!")); m_Controls.groupMasks->setVisible(false); } if (!m_Working) { this->m_Controls.m_leRegJobName->setText(QString::fromStdString(this->GetDefaultRegJobName())); } } void QmitkMatchPoint::ConfigureNodeSelectors() { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate(); auto isMask = mitk::MITKRegistrationHelper::MaskNodePredicate(); mitk::NodePredicateBase::Pointer dimensionPredicate = mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)).GetPointer(); m_Controls.movingNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.targetNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.movingMaskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.targetMaskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); if (m_LoadedAlgorithm.IsNotNull()) { mitk::NodePredicateBase::ConstPointer dataPredicate; if (m_LoadedAlgorithm->getMovingDimensions() == 2) { dimensionPredicate = mitk::NodePredicateDimension::New(2); } - if (mitk::MITKAlgorithmHelper::HasImageAlgorithmInterface(m_LoadedAlgorithm)) + if (mitk::MAPAlgorithmHelper::HasImageAlgorithmInterface(m_LoadedAlgorithm)) { dataPredicate = mitk::NodePredicateAnd::New(isImage, dimensionPredicate); m_Controls.movingNodeSelector->SetInvalidInfo("Select valid moving image."); m_Controls.movingNodeSelector->SetPopUpTitel("Select moving image."); m_Controls.movingNodeSelector->SetPopUpHint("Select the moving image that should be registered onto the target image."); m_Controls.targetNodeSelector->SetInvalidInfo("Select valid target image."); m_Controls.targetNodeSelector->SetPopUpTitel("Select target image."); m_Controls.targetNodeSelector->SetPopUpHint("Select the target image that should be used as reference for the registration."); } - if (mitk::MITKAlgorithmHelper::HasPointSetAlgorithmInterface(m_LoadedAlgorithm)) + if (mitk::MAPAlgorithmHelper::HasPointSetAlgorithmInterface(m_LoadedAlgorithm)) { if (dataPredicate.IsNull()) { dataPredicate = isPointSet; m_Controls.movingNodeSelector->SetInvalidInfo("Select valid moving point set."); m_Controls.movingNodeSelector->SetPopUpTitel("Select moving point set."); m_Controls.movingNodeSelector->SetPopUpHint("Select the moving point set that should be registered onto the target point set."); m_Controls.targetNodeSelector->SetInvalidInfo("Select valid target point set."); m_Controls.targetNodeSelector->SetPopUpTitel("Select target point set."); m_Controls.targetNodeSelector->SetPopUpHint("Select the target point set that should be used as reference for the registration."); } else { dataPredicate = mitk::NodePredicateOr::New(dataPredicate, isPointSet); m_Controls.movingNodeSelector->SetInvalidInfo("Select valid moving data."); m_Controls.movingNodeSelector->SetPopUpTitel("Select moving data."); m_Controls.movingNodeSelector->SetPopUpHint("Select the moving data that should be registered onto the target data. The algorithm supports images as well as point sets."); m_Controls.targetNodeSelector->SetInvalidInfo("Select valid target data."); m_Controls.targetNodeSelector->SetPopUpTitel("Select target data."); m_Controls.targetNodeSelector->SetPopUpHint("Select the target data that should be used as reference for the registration. The algorithm supports images as well as point sets."); } } mitk::NodePredicateBase::ConstPointer nodePredicate = dataPredicate; m_Controls.movingNodeSelector->SetNodePredicate(nodePredicate); m_Controls.targetNodeSelector->SetNodePredicate(nodePredicate); nodePredicate = mitk::NodePredicateAnd::New(isMask, dimensionPredicate); m_Controls.movingMaskNodeSelector->SetEmptyInfo("Select moving mask. (optional)"); m_Controls.movingMaskNodeSelector->SetPopUpTitel("Select moving mask"); m_Controls.movingMaskNodeSelector->SetPopUpHint("Select a segmentation that serves as moving mask for the registration."); m_Controls.targetMaskNodeSelector->SetEmptyInfo("Select target mask. (optional)"); m_Controls.targetMaskNodeSelector->SetPopUpTitel("Select target mask"); m_Controls.targetMaskNodeSelector->SetPopUpHint("Select a segmentation that serves as target mask for the registration."); m_Controls.movingMaskNodeSelector->SetNodePredicate(nodePredicate); m_Controls.targetMaskNodeSelector->SetNodePredicate(nodePredicate); } } void QmitkMatchPoint::ConfigureProgressInfos() { const IIterativeAlgorithm* pIterative = dynamic_cast (m_LoadedAlgorithm.GetPointer()); const IMultiResAlgorithm* pMultiRes = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.m_progBarIteration->setVisible(pIterative); m_Controls.m_lbProgBarIteration->setVisible(pIterative); if (pIterative) { QString format = "%p% (%v/%m)"; if (!pIterative->hasMaxIterationCount()) { format = "%v"; m_Controls.m_progBarIteration->setMaximum(0); } else { m_Controls.m_progBarIteration->setMaximum(pIterative->getMaxIterations()); } m_Controls.m_progBarIteration->setFormat(format); } m_Controls.m_progBarLevel->setVisible(pMultiRes); m_Controls.m_lbProgBarLevel->setVisible(pMultiRes); if (pMultiRes) { m_Controls.m_progBarLevel->setMaximum(pMultiRes->getResolutionLevels()); } else { m_Controls.m_progBarLevel->setMaximum(1); } m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); } void QmitkMatchPoint::OnNodeSelectionChanged(QList /*nodes*/) { if (!m_Working) { CheckInputs(); ConfigureRegistrationControls(); } } void QmitkMatchPoint::OnStartRegBtnPushed() { this->m_Working = true; //////////////////////////////// //configure GUI this->ConfigureProgressInfos(); m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); this->ConfigureRegistrationControls(); if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkRegistrationJob* pJob = new QmitkRegistrationJob(m_LoadedAlgorithm); pJob->setAutoDelete(true); pJob->m_spTargetData = m_spSelectedTargetData; pJob->m_spMovingData = m_spSelectedMovingData; pJob->m_TargetDataUID = mitk::EnsureUID(this->m_spSelectedTargetNode->GetData()); pJob->m_MovingDataUID = mitk::EnsureUID(this->m_spSelectedMovingNode->GetData()); if (m_spSelectedTargetMaskData.IsNotNull()) { pJob->m_spTargetMask = m_spSelectedTargetMaskData; pJob->m_TargetMaskDataUID = mitk::EnsureUID(this->m_spSelectedTargetMaskNode->GetData()); } if (m_spSelectedMovingMaskData.IsNotNull()) { pJob->m_spMovingMask = m_spSelectedMovingMaskData; pJob->m_MovingMaskDataUID = mitk::EnsureUID(this->m_spSelectedMovingMaskNode->GetData()); } pJob->m_JobName = m_Controls.m_leRegJobName->text().toStdString(); pJob->m_StoreReg = true; connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnRegJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnRegJobFinished())); connect(pJob, SIGNAL(RegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), this, SLOT(OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString))); connect(pJob, SIGNAL(AlgorithmStatusChanged(QString)), this, SLOT(OnAlgorithmStatusChanged(QString))); connect(pJob, SIGNAL(AlgorithmIterated(QString, bool, unsigned long)), this, SLOT(OnAlgorithmIterated(QString, bool, unsigned long))); connect(pJob, SIGNAL(LevelChanged(QString, bool, unsigned long)), this, SLOT(OnLevelChanged(QString, bool, unsigned long))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPoint::OnStopRegBtnPushed() { if (m_LoadedAlgorithm.IsNotNull()) { IStoppableAlgorithm* pIterativ = dynamic_cast(m_LoadedAlgorithm.GetPointer()); if (pIterativ && pIterativ->isStoppable()) { if (pIterativ->stopAlgorithm()) { } else { } m_Controls.m_pbStopReg->setEnabled(false); } else { } } } void QmitkMatchPoint::OnSaveLogBtnPushed() { QDateTime currentTime = QDateTime::currentDateTime(); QString fileName = tr("registration_log_") + currentTime.toString(tr("yyyy-MM-dd_hh-mm-ss")) + tr(".txt"); fileName = QFileDialog::getSaveFileName(nullptr, tr("Save registration log"), fileName, tr("Text files (*.txt)")); if (fileName.isEmpty()) { QMessageBox::critical(nullptr, tr("No file selected!"), tr("Cannot save registration log file. Please selected a file.")); } else { std::ofstream file; std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; file.open(fileName.toStdString().c_str(), iOpenFlag); if (!file.is_open()) { mitkThrow() << "Cannot open or create specified file to save. File path: " << fileName.toStdString(); } file << this->m_Controls.m_teLog->toPlainText().toStdString() << std::endl; file.close(); } } void QmitkMatchPoint::OnRegJobError(QString err) { Error(err); } void QmitkMatchPoint::OnRegJobFinished() { this->m_Working = false; this->GetRenderWindowPart()->RequestUpdate(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); } void QmitkMatchPoint::OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer spResultRegistration, const QmitkRegistrationJob* pRegJob) { mitk::DataNode::Pointer spResultRegistrationNode = mitk::generateRegistrationResultNode( pRegJob->m_JobName, spResultRegistration, pRegJob->GetLoadedAlgorithm()->getUID()->toStr(), pRegJob->m_MovingDataUID, pRegJob->m_TargetDataUID); if (pRegJob->m_StoreReg) { m_Controls.m_teLog->append( QStringLiteral(" Storing registration object in data manager ... ")); this->GetDataStorage()->Add(spResultRegistrationNode); this->GetRenderWindowPart()->RequestUpdate(); } if (m_Controls.m_checkMapEntity->checkState() == Qt::Checked) { QmitkMappingJob* pMapJob = new QmitkMappingJob(); pMapJob->setAutoDelete(true); pMapJob->m_spInputData = pRegJob->m_spMovingData; pMapJob->m_InputDataUID = pRegJob->m_MovingDataUID; pMapJob->m_spRegNode = spResultRegistrationNode; pMapJob->m_doGeometryRefinement = false; pMapJob->m_spRefGeometry = pRegJob->m_spTargetData->GetGeometry()->Clone().GetPointer(); pMapJob->m_MappedName = pRegJob->m_JobName + std::string(" mapped moving data"); pMapJob->m_allowUndefPixels = true; pMapJob->m_paddingValue = 100; pMapJob->m_allowUnregPixels = true; pMapJob->m_errorValue = 200; pMapJob->m_InterpolatorLabel = "Linear Interpolation"; pMapJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; connect(pMapJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString))); connect(pMapJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); connect(pMapJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString))); m_Controls.m_teLog->append( QStringLiteral("Started mapping input data...")); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pMapJob); } } void QmitkMatchPoint::OnMapJobError(QString err) { Error(err); } void QmitkMatchPoint::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { m_Controls.m_teLog->append(QStringLiteral("Mapped entity stored. Name: ") + QString::fromStdString(job->m_MappedName) + QStringLiteral("")); mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPoint::OnAlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration) { if (hasIterationCount) { m_Controls.m_progBarIteration->setValue(currentIteration); } m_Controls.m_teLog->append(info); } void QmitkMatchPoint::OnLevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel) { if (hasLevelCount) { m_Controls.m_progBarLevel->setValue(currentLevel); } m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPoint::OnAlgorithmStatusChanged(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral(" ")); } void QmitkMatchPoint::OnAlgorithmInfo(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPoint::OnAlgorithmSelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart, const berry::ISelection::ConstPointer& selection) { // check for null selection if (selection.IsNull()) { return; } if (sourcepart != this) { UpdateAlgorithmSelection(selection); } } void QmitkMatchPoint::UpdateAlgorithmSelection(berry::ISelection::ConstPointer selection) { mitk::MAPAlgorithmInfoSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection) { mitk::MAPAlgorithmInfoSelection::AlgorithmInfoVectorType infoVector = currentSelection->GetSelectedAlgorithmInfo(); if (!infoVector.empty()) { // only the first selection is of interest, the rest will be skipped. this->m_SelectedAlgorithmInfo = infoVector[0]; } } this->OnSelectedAlgorithmChanged(); } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp b/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp index 628984328e..86ba3b923a 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp @@ -1,304 +1,304 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Mitk #include #include #include #include "mitkRegVisPropertyTags.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegEvaluationObject.h" #include "mitkRegistrationHelper.h" #include "mitkRegEvaluationMapper2D.h" -#include +#include // Qmitk #include "QmitkRenderWindow.h" #include "QmitkMatchPointRegistrationEvaluator.h" // Qt #include #include #include const std::string QmitkMatchPointRegistrationEvaluator::VIEW_ID = "org.mitk.views.matchpoint.registration.evaluator"; const std::string QmitkMatchPointRegistrationEvaluator::HelperNodeName = "RegistrationEvaluationHelper"; QmitkMatchPointRegistrationEvaluator::QmitkMatchPointRegistrationEvaluator() : m_Parent(nullptr), m_activeEvaluation(false), m_currentSelectedTimeStep(0) { m_currentSelectedPosition.Fill(0.0); } QmitkMatchPointRegistrationEvaluator::~QmitkMatchPointRegistrationEvaluator() { if (this->m_selectedEvalNode.IsNotNull() && this->GetDataStorage().IsNotNull()) { this->GetDataStorage()->Remove(this->m_selectedEvalNode); } } void QmitkMatchPointRegistrationEvaluator::SetFocus() { } void QmitkMatchPointRegistrationEvaluator::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); } void QmitkMatchPointRegistrationEvaluator::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(true); this->m_Controls.movingNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.movingNodeSelector->SetSelectionIsOptional(false); this->m_Controls.targetNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.targetNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration."); this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity. Select registration to change."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be evaluated. If no registration is selected, identity will be assumed for evaluation."); this->m_Controls.movingNodeSelector->SetInvalidInfo("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpTitel("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpHint("Select the moving image for the evaluation. This is the image that will be mapped by the registration."); this->m_Controls.targetNodeSelector->SetInvalidInfo("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpTitel("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpHint("Select the target image for the evaluation."); this->ConfigureNodePredicates(); connect(m_Controls.pbEval, SIGNAL(clicked()), this, SLOT(OnEvalBtnPushed())); connect(m_Controls.pbStop, SIGNAL(clicked()), this, SLOT(OnStopBtnPushed())); connect(m_Controls.evalSettings, SIGNAL(SettingsChanged(mitk::DataNode*)), this, SLOT(OnSettingsChanged(mitk::DataNode*))); connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged); connect(m_Controls.movingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged); connect(m_Controls.targetNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); m_selectedEvalNode = this->GetDataStorage()->GetNamedNode(HelperNodeName); this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationEvaluator::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkMatchPointRegistrationEvaluator::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkMatchPointRegistrationEvaluator::ConfigureNodePredicates() { this->m_Controls.registrationNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::RegNodePredicate()); this->m_Controls.movingNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); this->m_Controls.targetNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); } void QmitkMatchPointRegistrationEvaluator::CheckInputs() { if (!m_activeEvaluation) { this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedMovingNode = this->m_Controls.movingNodeSelector->GetSelectedNode(); this->m_spSelectedTargetNode = this->m_Controls.targetNodeSelector->GetSelectedNode(); if (this->m_spSelectedMovingNode.IsNull() && this->m_spSelectedRegNode.IsNotNull()) { mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgMovingData); if (uidProp) { //search for the moving node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer movingNode = this->GetDataStorage()->GetNode(predicate); if (movingNode.IsNotNull()) { this->m_spSelectedMovingNode = movingNode; QmitkSingleNodeSelectionWidget::NodeList selection({ movingNode }); this->m_Controls.movingNodeSelector->SetCurrentSelection(selection); } } } if (this->m_spSelectedTargetNode.IsNull() && this->m_spSelectedRegNode.IsNotNull()) { mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer targetNode = this->GetDataStorage()->GetNode(predicate); if (targetNode.IsNotNull()) { this->m_spSelectedTargetNode = targetNode; QmitkSingleNodeSelectionWidget::NodeList selection({ targetNode }); this->m_Controls.targetNodeSelector->SetCurrentSelection(selection); } } } } } void QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationEvaluator::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_spSelectedMovingNode || node == this->m_spSelectedRegNode || node == this->m_spSelectedTargetNode || node == this->m_selectedEvalNode) { if (node == this->m_selectedEvalNode) { this->m_selectedEvalNode = nullptr; } this->OnStopBtnPushed(); MITK_INFO << "Stopped current MatchPoint evaluation session, because at least one relevant node was removed from storage."; } } void QmitkMatchPointRegistrationEvaluator::ConfigureControls() { //config settings widget this->m_Controls.evalSettings->setVisible(m_activeEvaluation); this->m_Controls.pbEval->setEnabled(this->m_spSelectedMovingNode.IsNotNull() && this->m_spSelectedTargetNode.IsNotNull()); this->m_Controls.pbEval->setVisible(!m_activeEvaluation); this->m_Controls.pbStop->setVisible(m_activeEvaluation); this->m_Controls.registrationNodeSelector->setEnabled(!m_activeEvaluation); this->m_Controls.movingNodeSelector->setEnabled(!m_activeEvaluation); this->m_Controls.targetNodeSelector->setEnabled(!m_activeEvaluation); } void QmitkMatchPointRegistrationEvaluator::OnSliceChanged() { mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); unsigned int currentSelectedTimeStep = GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimeStep || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimeStep; m_currentPositionTime.Modified(); if (this->m_selectedEvalNode.IsNotNull()) { this->m_selectedEvalNode->SetProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::GenericProperty::New(currentSelectedPosition)); } } } void QmitkMatchPointRegistrationEvaluator::OnSettingsChanged(mitk::DataNode*) { this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPointRegistrationEvaluator::OnEvalBtnPushed() { //reinit view mitk::RenderingManager::GetInstance()->InitializeViews(m_spSelectedTargetNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); mitk::RegEvaluationObject::Pointer regEval = mitk::RegEvaluationObject::New(); mitk::MAPRegistrationWrapper::Pointer reg; if (m_spSelectedRegNode.IsNotNull()) { reg = dynamic_cast(this->m_spSelectedRegNode->GetData()); } else { //generate a dymme reg to use reg = mitk::GenerateIdentityRegistration3D(); } regEval->SetRegistration(reg); regEval->SetTargetNode(this->m_spSelectedTargetNode); regEval->SetMovingNode(this->m_spSelectedMovingNode); if (this->m_selectedEvalNode.IsNotNull()) { this->GetDataStorage()->Remove(this->m_selectedEvalNode); } this->m_selectedEvalNode = mitk::DataNode::New(); this->m_selectedEvalNode->SetData(regEval); mitk::RegEvaluationMapper2D::SetDefaultProperties(this->m_selectedEvalNode); this->m_selectedEvalNode->SetName(HelperNodeName); this->m_selectedEvalNode->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(this->m_selectedEvalNode); this->m_Controls.evalSettings->SetNode(this->m_selectedEvalNode); this->OnSliceChanged(); this->GetRenderWindowPart()->RequestUpdate(); this->m_activeEvaluation = true; this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationEvaluator::OnStopBtnPushed() { this->m_activeEvaluation = false; if (this->m_selectedEvalNode.IsNotNull()) { this->GetDataStorage()->Remove(this->m_selectedEvalNode); } this->m_selectedEvalNode = nullptr; this->m_Controls.evalSettings->SetNode(this->m_selectedEvalNode); this->CheckInputs(); this->ConfigureControls(); this->GetRenderWindowPart()->RequestUpdate(); } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp b/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp index 3b1e805017..4ac853520c 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp @@ -1,495 +1,492 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Mitk #include #include #include #include "mitkRegVisPropertyTags.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegEvaluationObject.h" #include "mitkRegistrationHelper.h" #include "mitkRegEvaluationMapper2D.h" -#include +#include #include #include // Qmitk #include "QmitkRenderWindow.h" #include "QmitkMatchPointRegistrationManipulator.h" #include // Qt #include #include #include #include //MatchPoint #include #include #include #include #include #include #include const std::string QmitkMatchPointRegistrationManipulator::VIEW_ID = "org.mitk.views.matchpoint.registration.manipulator"; const std::string QmitkMatchPointRegistrationManipulator::HelperNodeName = "RegistrationManipulationEvaluationHelper"; QmitkMatchPointRegistrationManipulator::QmitkMatchPointRegistrationManipulator() : m_Parent(nullptr), m_activeManipulation(false), m_currentSelectedTimeStep(0), m_internalUpdate(false) { m_currentSelectedPosition.Fill(0.0); } QmitkMatchPointRegistrationManipulator::~QmitkMatchPointRegistrationManipulator() { if (this->m_EvalNode.IsNotNull() && this->GetDataStorage().IsNotNull()) { this->GetDataStorage()->Remove(this->m_EvalNode); } } void QmitkMatchPointRegistrationManipulator::SetFocus() { } void QmitkMatchPointRegistrationManipulator::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); } void QmitkMatchPointRegistrationManipulator::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(false); this->m_Controls.movingNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.movingNodeSelector->SetSelectionIsOptional(false); this->m_Controls.targetNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.targetNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select base registration."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used as starting point for the manual manipulation."); this->m_Controls.movingNodeSelector->SetInvalidInfo("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpTitel("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpHint("Select the moving image for the evaluation. This is the image that will be mapped by the registration."); this->m_Controls.targetNodeSelector->SetInvalidInfo("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpTitel("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpHint("Select the target image for the evaluation."); this->ConfigureNodePredicates(); connect(m_Controls.pbStart, SIGNAL(clicked()), this, SLOT(OnStartBtnPushed())); connect(m_Controls.pbCancel, SIGNAL(clicked()), this, SLOT(OnCancelBtnPushed())); connect(m_Controls.pbStore, SIGNAL(clicked()), this, SLOT(OnStoreBtnPushed())); connect(m_Controls.evalSettings, SIGNAL(SettingsChanged(mitk::DataNode*)), this, SLOT(OnSettingsChanged(mitk::DataNode*))); connect(m_Controls.radioSelectedReg, SIGNAL(toggled(bool)), this, SLOT(OnRegSourceChanged())); connect(m_Controls.comboCenter, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCenterTypeChanged(int))); connect(m_Controls.manipulationWidget, SIGNAL(RegistrationChanged(map::core::RegistrationBase*)), this, SLOT(OnRegistrationChanged())); connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged); connect(m_Controls.movingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged); connect(m_Controls.targetNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); m_Controls.radioNewReg->setChecked(true); m_EvalNode = this->GetDataStorage()->GetNamedNode(HelperNodeName); this->CheckInputs(); this->StopSession(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkMatchPointRegistrationManipulator::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkMatchPointRegistrationManipulator::ConfigureNodePredicates() { this->m_Controls.registrationNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::RegNodePredicate()); this->m_Controls.movingNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); this->m_Controls.targetNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); } void QmitkMatchPointRegistrationManipulator::CheckInputs() { if (!m_activeManipulation) { this->m_SelectedPreRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_SelectedMovingNode = this->m_Controls.movingNodeSelector->GetSelectedNode(); this->m_SelectedTargetNode = this->m_Controls.targetNodeSelector->GetSelectedNode(); if (this->m_SelectedPreRegNode.IsNotNull()) { mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast(m_SelectedPreRegNode->GetData()); if (regWrapper) { this->m_SelectedPreReg = dynamic_cast(regWrapper->GetRegistration()); } } if (this->m_SelectedMovingNode.IsNull() && this->m_SelectedPreRegNode.IsNotNull()) { mitk::BaseProperty* uidProp = m_SelectedPreRegNode->GetData()->GetProperty(mitk::Prop_RegAlgMovingData); if (uidProp) { //search for the moving node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer movingNode = this->GetDataStorage()->GetNode(predicate); if (movingNode.IsNotNull()) { this->m_SelectedMovingNode = movingNode; QmitkSingleNodeSelectionWidget::NodeList selection({ movingNode }); this->m_Controls.movingNodeSelector->SetCurrentSelection(selection); } } } if (this->m_SelectedTargetNode.IsNull() && this->m_SelectedPreRegNode.IsNotNull()) { mitk::BaseProperty* uidProp = m_SelectedPreRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer targetNode = this->GetDataStorage()->GetNode(predicate); if (targetNode.IsNotNull()) { this->m_SelectedTargetNode = targetNode; QmitkSingleNodeSelectionWidget::NodeList selection({ targetNode }); this->m_Controls.targetNodeSelector->SetCurrentSelection(selection); } } } } } void QmitkMatchPointRegistrationManipulator::OnRegSourceChanged() { this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_SelectedMovingNode || node == this->m_SelectedTargetNode || node == this->m_EvalNode) { if (node == this->m_EvalNode) { this->m_EvalNode = nullptr; } if (this->m_activeManipulation) { MITK_INFO << "Stopped current MatchPoint manual registration session, because at least one relevant node was removed from storage."; } this->OnCancelBtnPushed(); } } void QmitkMatchPointRegistrationManipulator::ConfigureControls() { if (!m_activeManipulation) { QString name = "ManuelRegistration"; if (m_SelectedPreRegNode.IsNotNull()) { name = QString::fromStdString(m_SelectedPreRegNode->GetName()) + " manual refined"; } this->m_Controls.lbNewRegName->setText(name); } //config settings widget this->m_Controls.groupReg->setEnabled(!m_activeManipulation); this->m_Controls.pbStart->setEnabled(this->m_SelectedMovingNode.IsNotNull() && this->m_SelectedTargetNode.IsNotNull() && !m_activeManipulation && (this->m_Controls.radioNewReg->isChecked() || this->m_SelectedPreReg.IsNotNull())); this->m_Controls.lbNewRegName->setEnabled(m_activeManipulation); this->m_Controls.checkMapEntity->setEnabled(m_activeManipulation); this->m_Controls.tabWidget->setEnabled(m_activeManipulation); this->m_Controls.pbCancel->setEnabled(m_activeManipulation); this->m_Controls.pbStore->setEnabled(m_activeManipulation); this->m_Controls.registrationNodeSelector->setEnabled(!m_activeManipulation && this->m_Controls.radioSelectedReg->isChecked()); this->m_Controls.movingNodeSelector->setEnabled(!m_activeManipulation); this->m_Controls.targetNodeSelector->setEnabled(!m_activeManipulation); } void QmitkMatchPointRegistrationManipulator::InitSession() { if (this->m_Controls.radioNewReg->isChecked()) { //init to map the image centers auto movingCenter = m_SelectedMovingNode->GetData()->GetTimeGeometry()->GetCenterInWorld(); auto targetCenter = m_SelectedTargetNode->GetData()->GetTimeGeometry()->GetCenterInWorld(); this->m_Controls.manipulationWidget->Initialize(movingCenter, targetCenter); } else { //use selected pre registration as baseline m_Controls.manipulationWidget->Initialize(m_SelectedPreReg); } - this->m_CurrentRegistrationWrapper = mitk::MAPRegistrationWrapper::New(); this->m_CurrentRegistration = m_Controls.manipulationWidget->GetInterimRegistration(); - this->m_CurrentRegistrationWrapper->SetRegistration(m_CurrentRegistration); + this->m_CurrentRegistrationWrapper = mitk::MAPRegistrationWrapper::New(m_CurrentRegistration); this->m_Controls.comboCenter->setCurrentIndex(0); this->OnCenterTypeChanged(0); //reinit view mitk::RenderingManager::GetInstance()->InitializeViews(m_SelectedTargetNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); //generate evaluation node mitk::RegEvaluationObject::Pointer regEval = mitk::RegEvaluationObject::New(); regEval->SetRegistration(this->m_CurrentRegistrationWrapper); regEval->SetTargetNode(this->m_SelectedTargetNode); regEval->SetMovingNode(this->m_SelectedMovingNode); this->m_EvalNode = mitk::DataNode::New(); this->m_EvalNode->SetData(regEval); mitk::RegEvaluationMapper2D::SetDefaultProperties(this->m_EvalNode); this->m_EvalNode->SetName(HelperNodeName); this->m_EvalNode->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(this->m_EvalNode); this->m_Controls.evalSettings->SetNode(this->m_EvalNode); this->m_activeManipulation = true; } void QmitkMatchPointRegistrationManipulator::StopSession() { this->m_activeManipulation = false; if (this->m_EvalNode.IsNotNull()) { this->GetDataStorage()->Remove(this->m_EvalNode); } this->m_EvalNode = nullptr; this->m_CurrentRegistration = nullptr; this->m_CurrentRegistrationWrapper = nullptr; m_Controls.manipulationWidget->Initialize(); } void QmitkMatchPointRegistrationManipulator::OnRegistrationChanged() { if (this->m_EvalNode.IsNotNull()) { this->m_EvalNode->Modified(); } if (this->m_CurrentRegistrationWrapper.IsNotNull()) { this->m_CurrentRegistrationWrapper->Modified(); } this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnSliceChanged() { mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); unsigned int currentSelectedTimeStep = GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimeStep || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimeStep; m_currentPositionTime.Modified(); if (this->m_EvalNode.IsNotNull()) { this->m_EvalNode->SetProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::GenericProperty::New(currentSelectedPosition)); } if (m_activeManipulation && m_Controls.comboCenter->currentIndex() == 2) { //update transform with the current position. m_Controls.manipulationWidget->SetCenterOfRotation(m_currentSelectedPosition); } } } void QmitkMatchPointRegistrationManipulator::OnSettingsChanged(mitk::DataNode*) { this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnStartBtnPushed() { this->InitSession(); this->OnSliceChanged(); this->GetRenderWindowPart()->RequestUpdate(); this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::OnCancelBtnPushed() { this->StopSession(); this->CheckInputs(); this->ConfigureControls(); if (this->GetRenderWindowPart()) { this->GetRenderWindowPart()->RequestUpdate(); } } void QmitkMatchPointRegistrationManipulator::OnStoreBtnPushed() { - mitk::MAPRegistrationWrapper::Pointer newRegWrapper = mitk::MAPRegistrationWrapper::New(); map::core::RegistrationBase::Pointer newReg = this->m_Controls.manipulationWidget->GenerateRegistration(); - - newRegWrapper->SetRegistration(newReg); + auto newRegWrapper = mitk::MAPRegistrationWrapper::New(newReg); mitk::DataNode::Pointer spResultRegistrationNode = mitk::generateRegistrationResultNode( this->m_Controls.lbNewRegName->text().toStdString(), newRegWrapper, "org.mitk::manual_registration", mitk::EnsureUID(m_SelectedMovingNode->GetData()), mitk::EnsureUID(m_SelectedTargetNode->GetData())); this->GetDataStorage()->Add(spResultRegistrationNode); if (m_Controls.checkMapEntity->checkState() == Qt::Checked) { QmitkMappingJob* pMapJob = new QmitkMappingJob(); pMapJob->setAutoDelete(true); pMapJob->m_spInputData = this->m_SelectedMovingNode->GetData(); pMapJob->m_InputDataUID = mitk::EnsureUID(m_SelectedMovingNode->GetData()); pMapJob->m_spRegNode = spResultRegistrationNode; pMapJob->m_doGeometryRefinement = false; pMapJob->m_spRefGeometry = this->m_SelectedTargetNode->GetData()->GetGeometry()->Clone().GetPointer(); pMapJob->m_MappedName = this->m_Controls.lbNewRegName->text().toStdString() + std::string(" mapped moving data"); pMapJob->m_allowUndefPixels = true; pMapJob->m_paddingValue = 100; pMapJob->m_allowUnregPixels = true; pMapJob->m_errorValue = 200; pMapJob->m_InterpolatorLabel = "Linear Interpolation"; pMapJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; connect(pMapJob, SIGNAL(Error(QString)), this, SLOT(Error(QString))); connect(pMapJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pMapJob); } this->StopSession(); this->CheckInputs(); this->ConfigureControls(); this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnCenterTypeChanged(int index) { ConfigureTransformCenter(index); if (this->m_EvalNode.IsNotNull()) { this->m_EvalNode->Modified(); } if (this->m_CurrentRegistrationWrapper.IsNotNull()) { this->m_CurrentRegistrationWrapper->Modified(); } this->GetRenderWindowPart()->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::ConfigureTransformCenter(int centerType) { if (centerType == 0) { //image center auto center = m_SelectedMovingNode->GetData()->GetTimeGeometry()->GetCenterInWorld(); m_Controls.manipulationWidget->SetCenterOfRotationIsRelativeToTarget(false); m_Controls.manipulationWidget->SetCenterOfRotation(center); } else if (centerType == 1) { //world origin mitk::Point3D center; center.Fill(0.0); m_Controls.manipulationWidget->SetCenterOfRotationIsRelativeToTarget(false); m_Controls.manipulationWidget->SetCenterOfRotation(center); } else { //current selected point m_Controls.manipulationWidget->SetCenterOfRotationIsRelativeToTarget(true); m_Controls.manipulationWidget->SetCenterOfRotation(m_currentSelectedPosition); } } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp index cbf0d65d8d..31243b624a 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp @@ -1,580 +1,580 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "org_mitk_gui_qt_matchpoint_mapper_Activator.h" // Blueberry #include #include // Mitk #include #include #include "mitkImageMappingHelper.h" #include "mitkMAPRegistrationWrapper.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegistrationHelper.h" #include #include -#include +#include #include #include #include #include #include // Qmitk #include "QmitkMatchPointMapper.h" // Qt #include #include #include #include const std::string QmitkMatchPointMapper::VIEW_ID = "org.mitk.views.matchpoint.mapper"; QmitkMatchPointMapper::QmitkMatchPointMapper() : m_Parent(nullptr), m_preparedForBinaryInput(false) { } void QmitkMatchPointMapper::SetFocus() { //m_Controls.buttonPerformImageProcessing->setFocus(); } void QmitkMatchPointMapper::CreateConnections() { connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnRegNodeSelectionChanged); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnInputNodeSelectionChanged); connect(m_Controls.referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnReferenceNodeSelectionChanged); connect(m_Controls.m_cbManualRef, SIGNAL(clicked()), this, SLOT(OnManualRefChecked())); connect(m_Controls.m_cbLinkFactors, SIGNAL(clicked()), this, SLOT(OnLinkSampleFactorChecked())); connect(m_Controls.m_sbXFactor, SIGNAL(valueChanged(double)), this, SLOT(OnXFactorChanged(double))); connect(m_Controls.m_pbMap, SIGNAL(clicked()), this, SLOT(OnMapBtnPushed())); connect(m_Controls.m_pbRefine, SIGNAL(clicked()), this, SLOT(OnRefineBtnPushed())); } void QmitkMatchPointMapper::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QStringLiteral("") + msg + QStringLiteral("")); } void QmitkMatchPointMapper::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(true); this->m_Controls.inputNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.inputNodeSelector->SetSelectionIsOptional(false); this->m_Controls.referenceNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.referenceNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration."); this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity mapping. Select registration to change."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used for the mapping of the input data. If no registration is selected, identity will be assumed for the mapping."); this->m_Controls.inputNodeSelector->SetInvalidInfo("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpTitel("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpHint("Select the input data for the mapping. (Images or point sets are supported so far)."); this->m_Controls.referenceNodeSelector->SetInvalidInfo("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpTitel("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpHint("Select the reference image that specifies the target geometrie the input should be mapped into."); this->ConfigureRegNodePredicate(); this->ConfigureNodePredicates(); // show first page m_Controls.m_tabs->setCurrentIndex(0); this->CreateConnections(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureMappingControls(); } /** Method checks if the currently selected reg node has a direct kernel that * can be decomposed in a rotation matrix and a offset. If this is true, true * is returned. In all other cases false is returned.*/ bool QmitkMatchPointMapper::IsAbleToRefineGeometry() const { bool result = false; if (this->m_spSelectedRegNode.IsNotNull()) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (this->m_spSelectedRegNode->GetData()); //if the helper does not return null, we can refine the geometry. result = mitk::MITKRegistrationHelper::getAffineMatrix(wrapper, false).IsNotNull(); } return result; } bool QmitkMatchPointMapper::IsBinaryInput() const { auto maskPredicate = mitk::MITKRegistrationHelper::MaskNodePredicate(); bool result = false; if(this->m_spSelectedInputNode.IsNotNull()) { result = maskPredicate->CheckNode(this->m_spSelectedInputNode); } return result; } bool QmitkMatchPointMapper::IsPointSetInput() const { bool result = false; if (this->m_spSelectedInputNode.IsNotNull()) { result = dynamic_cast(this->m_spSelectedInputNode->GetData()) != nullptr; } return result; } mitk::DataNode::Pointer QmitkMatchPointMapper::GetAutoRefNodeByReg() { mitk::DataNode::Pointer spResult = nullptr; if (this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedRegNode->GetData()) { std::string nodeName; mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); spResult = this->GetDataStorage()->GetNode(predicate); } } if (spResult.IsNull() && this->m_spSelectedInputNode.IsNotNull()) { //no really reference is available -> use the input as reference spResult = this->m_spSelectedInputNode; if (this->m_spSelectedRefNode != spResult) { m_Controls.m_teLog->append( QStringLiteral("Cannot determine reference automatically. Use input image as reference.")); } } return spResult; } void QmitkMatchPointMapper::ConfigureRegNodePredicate(const mitk::DataNode* input) { mitk::NodePredicateBase::ConstPointer nodePredicate = mitk::MITKRegistrationHelper::RegNodePredicate(); if (input != nullptr) { unsigned int dimension = 0; auto inputImage = dynamic_cast(input->GetData()); auto pointset = dynamic_cast(input->GetData()); if (inputImage) { dimension = inputImage->GetDimension(); if (inputImage->GetTimeSteps() > 1) { //images has multiple time steps -> remove one dimension. dimension -= 1; } } else if (pointset) { dimension = 3; } auto dimCheck = [dimension](const mitk::DataNode * node) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast < const mitk::MAPRegistrationWrapper* >(node->GetData()); return wrapper != nullptr && wrapper->GetMovingDimensions() == dimension; }; mitk::NodePredicateFunction::Pointer hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); nodePredicate = mitk::NodePredicateAnd::New(nodePredicate, hasCorrectDim).GetPointer(); } this->m_Controls.registrationNodeSelector->SetNodePredicate(nodePredicate); } std::function GenerateDimCheckLambda(unsigned int dim) { auto dimCheck = [dim](const mitk::DataNode * node) { auto inputImage = dynamic_cast(node->GetData()); return inputImage != nullptr && (inputImage->GetDimension() == dim || (inputImage->GetDimension() == dim + 1 && inputImage->GetTimeSteps()>1)); }; return dimCheck; } void QmitkMatchPointMapper::ConfigureNodePredicates(const mitk::DataNode* reg) { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate(); auto isData = mitk::NodePredicateOr::New(isImage, isPointSet); mitk::NodePredicateBase::ConstPointer inputPredicate = isData.GetPointer(); mitk::NodePredicateBase::ConstPointer refPredicate = isImage.GetPointer(); if (reg != nullptr) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (reg->GetData()); if (wrapper != nullptr) { auto movingDim = wrapper->GetMovingDimensions(); auto dimCheck = GenerateDimCheckLambda(movingDim); auto hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); if (movingDim == 3) { //Remark: Point sets are always 3D auto is3DInput = mitk::NodePredicateOr::New(isPointSet, mitk::NodePredicateAnd::New(isImage, hasCorrectDim)); inputPredicate = is3DInput.GetPointer(); } else { auto is2DInput = mitk::NodePredicateAnd::New(isImage, hasCorrectDim); inputPredicate = is2DInput.GetPointer(); } auto targetDim = wrapper->GetTargetDimensions(); auto targetDimCheck = GenerateDimCheckLambda(targetDim); auto hasCorrectTargetDim = mitk::NodePredicateFunction::New(targetDimCheck); auto isRef = mitk::NodePredicateAnd::New(isImage, hasCorrectTargetDim); refPredicate = isRef; } } this->m_Controls.inputNodeSelector->SetNodePredicate(inputPredicate); this->m_Controls.referenceNodeSelector->SetNodePredicate(refPredicate); } void QmitkMatchPointMapper::CheckInputs() { this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedInputNode = this->m_Controls.inputNodeSelector->GetSelectedNode(); this->m_spSelectedRefNode = this->m_Controls.referenceNodeSelector->GetSelectedNode(); if (!(m_Controls.m_cbManualRef->isChecked())) { auto autoRefNode = this->GetAutoRefNodeByReg(); if (this->m_spSelectedRefNode != autoRefNode) { this->m_spSelectedRefNode = autoRefNode; QmitkSingleNodeSelectionWidget::NodeList selection; if (this->m_spSelectedRefNode.IsNotNull()) { selection.append(this->m_spSelectedRefNode); } this->m_Controls.referenceNodeSelector->SetCurrentSelection(selection); } } if (this->m_spSelectedRefNode.IsNotNull() && this->m_spSelectedRefNode->GetData() && this->m_spSelectedRefNode->GetData()->GetTimeSteps() > 1) { m_Controls.m_teLog->append( QStringLiteral("Selected reference image has multiple time steps. Only geometry of time step 1 is used as reference.")); } } void QmitkMatchPointMapper::ConfigureMappingControls() { bool validInput = m_spSelectedInputNode.IsNotNull(); bool validRef = m_spSelectedRefNode.IsNotNull(); this->m_Controls.referenceNodeSelector->setEnabled(this->m_Controls.m_cbManualRef->isChecked()); this->m_Controls.m_pbMap->setEnabled(validInput && validRef); this->m_Controls.m_pbRefine->setEnabled(validInput && this->IsAbleToRefineGeometry() && !this->IsPointSetInput()); if (validInput) { if (m_spSelectedRegNode.IsNotNull()) { this->m_Controls.m_leMappedName->setText(tr("mapped_") + QString::fromStdString(m_spSelectedInputNode->GetName()) + tr("_by_") + QString::fromStdString(m_spSelectedRegNode->GetName())); } else { this->m_Controls.m_leMappedName->setText(tr("resampled_") + QString::fromStdString(m_spSelectedInputNode->GetName())); } } else { this->m_Controls.m_leMappedName->setText(tr("mappedData")); } if (this->IsBinaryInput() != this->m_preparedForBinaryInput) { if (this->IsBinaryInput()) { m_Controls.m_teLog->append( QStringLiteral("Binary input (mask) detected. Preparing for mask mapping (default interpolation: nearest neigbour; padding value: 0)")); this->m_Controls.m_comboInterpolator->setCurrentIndex(0); this->m_Controls.m_sbErrorValue->setValue(0); this->m_Controls.m_sbPaddingValue->setValue(0); } else { this->m_Controls.m_comboInterpolator->setCurrentIndex(1); } this->m_preparedForBinaryInput = this->IsBinaryInput(); } OnLinkSampleFactorChecked(); } void QmitkMatchPointMapper::ConfigureProgressInfos() { } void QmitkMatchPointMapper::OnRegNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer regNode; if (!nodes.isEmpty()) { regNode = nodes.front(); } this->ConfigureNodePredicates(regNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnInputNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer inputNode; if (!nodes.isEmpty()) { inputNode = nodes.front(); } this->ConfigureRegNodePredicate(inputNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnReferenceNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnManualRefChecked() { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnLinkSampleFactorChecked() { this->m_Controls.m_sbYFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); this->m_Controls.m_sbZFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(this->m_Controls.m_sbXFactor->value()); this->m_Controls.m_sbZFactor->setValue(this->m_Controls.m_sbXFactor->value()); } } void QmitkMatchPointMapper::OnMapBtnPushed() { SpawnMappingJob(); } void QmitkMatchPointMapper::OnRefineBtnPushed() { SpawnMappingJob(true); } void QmitkMatchPointMapper::SpawnMappingJob(bool doGeometryRefinement) { if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkMappingJob* pJob = new QmitkMappingJob(); pJob->setAutoDelete(true); pJob->m_spInputData = this->m_spSelectedInputNode->GetData(); pJob->m_InputDataUID = mitk::EnsureUID(this->m_spSelectedInputNode->GetData()); pJob->m_doGeometryRefinement = doGeometryRefinement; pJob->m_spRegNode = m_spSelectedRegNode; if (m_spSelectedRegNode.IsNull()) { pJob->m_spRegNode = mitk::DataNode::New(); pJob->m_spRegNode->SetData(mitk::GenerateIdentityRegistration3D().GetPointer()); pJob->m_spRegNode->SetName("Auto_Generated_Identity_Transform"); m_Controls.m_teLog->append( QStringLiteral("No registration selected. Preforming mapping with identity transform")); } if (!doGeometryRefinement) { pJob->m_spRefGeometry = m_spSelectedRefNode->GetData()->GetGeometry()->Clone().GetPointer(); //check for super/sub sampling if (m_Controls.m_groupActivateSampling->isChecked()) { //change the pixel count and spacing of the geometry mitk::BaseGeometry::BoundsArrayType geoBounds = pJob->m_spRefGeometry->GetBounds(); auto oldSpacing = pJob->m_spRefGeometry->GetSpacing(); mitk::Vector3D geoSpacing; geoSpacing[0] = oldSpacing[0] / m_Controls.m_sbXFactor->value(); geoSpacing[1] = oldSpacing[1] / m_Controls.m_sbYFactor->value(); geoSpacing[2] = oldSpacing[2] / m_Controls.m_sbZFactor->value(); geoBounds[1] = geoBounds[1] * m_Controls.m_sbXFactor->value(); geoBounds[3] = geoBounds[3] * m_Controls.m_sbYFactor->value(); geoBounds[5] = geoBounds[5] * m_Controls.m_sbZFactor->value(); pJob->m_spRefGeometry->SetBounds(geoBounds); pJob->m_spRefGeometry->SetSpacing(geoSpacing); auto oldOrigin = pJob->m_spRefGeometry->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]; } pJob->m_spRefGeometry->SetOrigin(newOrigin); } } pJob->m_MappedName = m_Controls.m_leMappedName->text().toStdString(); pJob->m_allowUndefPixels = m_Controls.m_groupAllowUndefPixels->isChecked(); pJob->m_paddingValue = m_Controls.m_sbPaddingValue->value(); pJob->m_allowUnregPixels = m_Controls.m_groupAllowUnregPixels->isChecked(); pJob->m_errorValue = m_Controls.m_sbErrorValue->value(); pJob->m_InterpolatorLabel = m_Controls.m_comboInterpolator->currentText().toStdString(); switch (m_Controls.m_comboInterpolator->currentIndex()) { case 0: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::NearestNeighbor; break; case 1: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; break; case 2: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::BSpline_3; break; case 3: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Hamming; break; case 4: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Welch; break; } connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString))); connect(pJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnMappingInfo(QString))); m_Controls.m_teLog->append(QStringLiteral("Started mapping job. Name: ") + m_Controls.m_leMappedName->text() + QStringLiteral("")); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPointMapper::OnMapJobError(QString err) { Error(err); } void QmitkMatchPointMapper::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { m_Controls.m_teLog->append(QStringLiteral("Mapped entity stored. Name: ") + QString::fromStdString(job->m_MappedName) + QStringLiteral("")); mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); this->GetRenderWindowPart()->RequestUpdate(); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnMappingInfo(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPointMapper::OnXFactorChanged(double d) { if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(d); this->m_Controls.m_sbZFactor->setValue(d); } } diff --git a/Wrapping/Common/mitk_swig_classes.i b/Wrapping/Common/mitk_swig_classes.i index 89bfc0fad8..9241ec56b9 100644 --- a/Wrapping/Common/mitk_swig_classes.i +++ b/Wrapping/Common/mitk_swig_classes.i @@ -1,157 +1,157 @@ // // Defining some Macros that make problems with SWIG as the // corresponding definitions are not included by default. // Luckely, these includes are not necessary for SWIG. // #define ITK_NOEXCEPT #define ITKCommon_EXPORT #define ITK_OVERRIDE #define MITKCORE_EXPORT #define MITKCLCORE_EXPORT #define MITKCLUTILITIES_EXPORT #define ITKCommon_EXPORT #define MITKMATCHPOINTREGISTRATION_EXPORT #define MAPDeployment_EXPORT #define MAPAlgorithms_EXPORT #define MITKSEGMENTATION_EXPORT #define MITKMULTILABEL_EXPORT #define ITKCommon_EXPORT #define ITK_FORWARD_EXPORT #define ITK_OVERRIDE #define ITK_NOEXCEPT %include %include %include %include %include %include #define DEPRECATED(func) func #undef ITK_DISALLOW_COPY_AND_ASSIGN #define ITK_DISALLOW_COPY_AND_ASSIGN(TypeName) %pythoncode %{ convertion_list = {} %} SWIG_ADD_NONOBJECT_CLASS(Indent, itkIndent.h, itk) SWIG_ADD_MITK_CLASS(LightObject, itkLightObject.h, itk) SWIG_ADD_MITK_CLASS(Object, itkObject.h, itk) SWIG_ADD_MITK_CLASS(DataObject, itkDataObject.h, itk) SWIG_ADD_MITK_CLASS(TimeGeometry, mitkTimeGeometry.h, mitk) SWIG_ADD_MITK_CLASS(ArbitraryTimeGeometry, mitkArbitraryTimeGeometry.h, mitk) SWIG_ADD_MITK_CLASS(ProportionalTimeGeometry, mitkProportionalTimeGeometry.h, mitk) SWIG_ADD_MITK_CLASS(BaseGeometry, mitkBaseGeometry.h, mitk) SWIG_ADD_MITK_CLASS(Geometry3D, mitkGeometry3D.h, mitk) SWIG_ADD_MITK_CLASS(SlicedGeometry3D, mitkSlicedGeometry3D.h, mitk) SWIG_ADD_MITK_CLASS(PlaneGeometry , mitkPlaneGeometry.h, mitk) SWIG_ADD_NONOBJECT_NOVECTOR_CLASS(BoundingBox, mitkBaseGeometry.h, mitk) SWIG_ADD_NONOBJECT_CLASS(TimeBounds, mitkBaseGeometry.h, mitk) SWIG_ADD_NONOBJECT_CLASS(FixedArrayType, mitkBaseGeometry.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point2D, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point3D, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point4D, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point2I, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point3I, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Point4I, mitkPoint.h, mitk) SWIG_ADD_NONOBJECT_CLASS(VnlVector, mitkVector.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Vector2D, mitkVector.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Vector3D, mitkVector.h, mitk) SWIG_ADD_NONOBJECT_CLASS(Vector4D, mitkVector.h, mitk) SWIG_ADD_MITK_CLASS(BaseData, mitkBaseData.h, mitk) SWIG_ADD_MITK_CLASS(SlicedData, mitkSlicedData.h, mitk) SWIG_ADD_MITK_CLASS(Image, mitkImage.h, mitk) SWIG_ADD_MITK_CLASS(LabelSetImage, mitkLabelSetImage.h, mitk) SWIG_ADD_MITK_CLASS(PointSet, mitkPointSet.h, mitk) %{ using mitk::Message; %} // // Phenotyping Related Classes // SWIG_ADD_MITK_CLASS(AbstractGlobalImageFeature, mitkAbstractGlobalImageFeature.h, mitk) SWIG_ADD_MITK_CLASS(GIFImageDescriptionFeatures, mitkGIFImageDescriptionFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFFirstOrderStatistics, mitkGIFFirstOrderStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFFirstOrderHistogramStatistics, mitkGIFFirstOrderHistogramStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFVolumetricStatistics, mitkGIFVolumetricStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFVolumetricDensityStatistics, mitkGIFVolumetricDensityStatistics.h, mitk) SWIG_ADD_MITK_CLASS(GIFCooccurenceMatrix2, mitkGIFCooccurenceMatrix2.h, mitk) SWIG_ADD_MITK_CLASS(GIFNeighbouringGreyLevelDependenceFeature, mitkGIFNeighbouringGreyLevelDependenceFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFGreyLevelRunLength, mitkGIFGreyLevelRunLength.h, mitk) SWIG_ADD_MITK_CLASS(GIFGreyLevelSizeZone, mitkGIFGreyLevelSizeZone.h, mitk) SWIG_ADD_MITK_CLASS(GIFGreyLevelDistanceZone, mitkGIFGreyLevelDistanceZone.h, mitk) SWIG_ADD_MITK_CLASS(GIFLocalIntensity, mitkGIFLocalIntensity.h, mitk) SWIG_ADD_MITK_CLASS(GIFIntensityVolumeHistogramFeatures, mitkGIFIntensityVolumeHistogramFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFNeighbourhoodGreyToneDifferenceFeatures, mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h, mitk) SWIG_ADD_MITK_CLASS(GIFCurvatureStatistic, mitkGIFCurvatureStatistic.h, mitk) // // Conversion and Segmentation based Classes // SWIG_ADD_MITK_CLASS(ContourModelSetToImageFilter, mitkContourModelSetToImageFilter.h, mitk) SWIG_ADD_NONOBJECT_NOVECTOR_CLASS(BooleanOperation, mitkBooleanOperation.h, mitk) SWIG_ADD_NONOBJECT_NOVECTOR_CLASS(MorphologicalOperations, mitkMorphologicalOperations.h, mitk) %{ #include typedef itk::DataObject::DataObjectIdentifierType DataObjectIdentifierType; typedef itk::ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; %} // // MatchPoint Related Classes // %ignore DLLHandle::LibraryHandleType; %{ #include namespace core { typedef std::string String; } typedef map::deployment::DLLHandle::LibraryHandleType LibraryHandleType; typedef map::deployment::DLLHandle DLLHandle; %} namespace core { typedef std::string String; } %nodefaultctor LibraryHandleType; struct LibraryHandleType {}; %include SWIG_ADD_MITK_CLASS(UID,mapUID.h, map::algorithm) SWIG_ADD_MITK_CLASS(DLLInfo, mapDeploymentDLLInfo.h, map::deployment) //SWIG_ADD_MITK_CLASS_VECTORFREE(DLLHandle, mapDeploymentDLLHandle.h, map::deployment) SWIG_ADD_MITK_CLASS_VECTORFREE(DLLDirectoryBrowser, mapDeploymentDLLDirectoryBrowser.h, ::map::deployment) MITKSWIG_ADD_HEADERFILE(mapDeploymentDLLAccess.h) %{ DLLHandle const * ConvertDLLHandleSmartPointerToPointer(itk::SmartPointer p) { return p.GetPointer(); } %} DLLHandle const * ConvertDLLHandleSmartPointerToPointer(DLLHandle::Pointer p); -MITKSWIG_ADD_CLASS(MITKAlgorithmHelper, mitkAlgorithmHelper.h, mitk) +MITKSWIG_ADD_CLASS(MAPAlgorithmHelper, mitkMAPAlgorithmHelper.h, mitk) MITKSWIG_ADD_CLASS(RegistrationType, mitkImageMappingHelper.h, mitk::ImageMappingHelper) MITKSWIG_ADD_CLASS(MITKRegistrationType, mitkImageMappingHelper.h, mitk::ImageMappingHelper) // SWIG_ADD_MITK_CLASS(FastSymmetricForcesDemonsMultiResDefaultRegistrationAlgorithm, mitkFastSymmetricForcesDemonsMultiResDefaultRegistrationAlgorithm.h, mitk) // SWIG_ADD_MITK_CLASS(LevelSetMotionMultiResDefaultRegistrationAlgorithm, mitkLevelSetMotionMultiResDefaultRegistrationAlgorithm.h, mitk) // SWIG_ADD_MITK_CLASS(MultiModalAffineDefaultRegistrationAlgorithm, mitkMultiModalAffineDefaultRegistrationAlgorithm.h, mitk) // SWIG_ADD_MITK_CLASS(MultiModalRigidDefaultRegistrationAlgorithm, mitkMultiModalRigidDefaultRegistrationAlgorithm.h, mitk) // SWIG_ADD_MITK_CLASS(MultiModalTransDefaultRegistrationAlgorithm, mitkMultiModalTransDefaultRegistrationAlgorithm.h, mitk) // SWIG_ADD_MITK_CLASS(RigidClosedFormPointsDefaultRegistrationAlgorithm, mitkRigidClosedFormPointsDefaultRegistrationAlgorithm.h, mitk) // SWIG_ADD_MITK_CLASS(RigidICPDefaultRegistrationAlgorithm, mitkRigidICPDefaultRegistrationAlgorithm.h, mitk) \ No newline at end of file