diff --git a/Modules/AlgorithmsExt/files.cmake b/Modules/AlgorithmsExt/files.cmake index 55da8aeb07..3e8bacd12d 100644 --- a/Modules/AlgorithmsExt/files.cmake +++ b/Modules/AlgorithmsExt/files.cmake @@ -1,36 +1,37 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkAutoCropImageFilter.cpp mitkBoundingObjectCutter.cpp mitkBoundingObjectToSegmentationFilter.cpp mitkGeometryClipImageFilter.cpp mitkGeometryDataSource.cpp mitkHeightFieldSurfaceClipImageFilter.cpp mitkImageToUnstructuredGridFilter.cpp mitkLabeledImageToSurfaceFilter.cpp mitkMaskAndCutRoiImageFilter.cpp mitkMaskImageFilter.cpp mitkMovieGenerator.cpp mitkNonBlockingAlgorithm.cpp mitkPadImageFilter.cpp mitkPlaneFit.cpp mitkPlaneLandmarkProjector.cpp mitkPointLocator.cpp mitkSegmentationSink.cpp mitkSimpleHistogram.cpp mitkSimpleUnstructuredGridHistogram.cpp mitkCovarianceMatrixCalculator.cpp mitkAnisotropicIterativeClosestPointRegistration.cpp mitkWeightedPointTransform.cpp mitkAnisotropicRegistrationCommon.cpp mitkUnstructuredGridClusteringFilter.cpp mitkUnstructuredGridToUnstructuredGridFilter.cpp mitkSurfaceToPointSetFilter.cpp + mitkCropTimestepsImageFilter.cpp ) if(WIN32) list(APPEND CPP_FILES mitkMovieGeneratorWin32.cpp ) endif() diff --git a/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h new file mode 100644 index 0000000000..f2cbe5496b --- /dev/null +++ b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h @@ -0,0 +1,84 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkCropTimestepsImageFilter_h +#define mitkCropTimestepsImageFilter_h + +#include "MitkAlgorithmsExtExports.h" + +#include + +namespace mitk +{ + /** \brief Crops timesteps at 2D+t and 3D+t images + * \details The filter is able to crop timesteps in front and/or at the end. + * Internally, a new image is created with the remaining volumes. The geometries and properties of + * the input image are transferred to the output image. + */ + class MITKALGORITHMSEXT_EXPORT CropTimestepsImageFilter : public SubImageSelector + { + public: + mitkClassMacro(CropTimestepsImageFilter, SubImageSelector) + itkFactorylessNewMacro(Self) + + /*! + * \brief Sets the input image + * \pre the image has 4 Dimensions + * \pre the image has >1 timestep + * \pre the image is valid (valid geometry and volume) + */ + void SetInput(const InputImageType* image) override; + void SetInput(unsigned int index, const InputImageType* image) override; + + /*! + * \brief The last timestep to retain + * \details Set to the maximum timestep of the input as default + * \note if the last timestep is larger than the maximum timestep of the input, + * it is corrected to the maximum timestep. + * \exception if (LowerBoundaryTimestep > UpperBoundaryTimestep) + */ + itkSetMacro(UpperBoundaryTimestep, unsigned int); + itkGetConstMacro(UpperBoundaryTimestep, unsigned int); + + /*! + * \brief The first timestep to retain + * \details Set to 0 as default + * \exception if (LowerBoundaryTimestep > UpperBoundaryTimestep) + */ + itkSetMacro(LowerBoundaryTimestep, unsigned int); + itkGetConstMacro(LowerBoundaryTimestep, unsigned int); + + private: + using Superclass::SetInput; + + CropTimestepsImageFilter() = default; + ~CropTimestepsImageFilter() override = default; + + void GenerateData() override; + void VerifyInputInformation() override; + void VerifyInputImage(const mitk::Image* inputImage) const; + void GenerateOutputInformation() override; + mitk::SlicedData::RegionType ComputeDesiredRegion() const; + mitk::TimeGeometry::Pointer AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const; + + unsigned int m_UpperBoundaryTimestep = std::numeric_limits::max(); + unsigned int m_LowerBoundaryTimestep = 0; + + mitk::SlicedData::RegionType m_DesiredRegion; + }; +} + +#endif diff --git a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp new file mode 100644 index 0000000000..c68dffe8b7 --- /dev/null +++ b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp @@ -0,0 +1,151 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkCropTimestepsImageFilter.h" + +#include +#include +#include +#include + + + void mitk::CropTimestepsImageFilter::VerifyInputImage(const mitk::Image* inputImage) const + { + if (!inputImage->IsInitialized()) + mitkThrow() << "Input image is not initialized."; + + if (!inputImage->IsVolumeSet()) + mitkThrow() << "Input image volume is not set."; + + auto geometry = inputImage->GetGeometry(); + + if (nullptr == geometry || !geometry->IsValid()) + mitkThrow() << "Input image has invalid geometry."; + + if (inputImage->GetDimension() != 4) { + mitkThrow() << "CropTimestepsImageFilter only works with 2D+t and 3D+t images."; + } + + if (inputImage->GetTimeSteps() ==1) { + mitkThrow() << "Input image has only one timestep."; + } + + if (!geometry->GetImageGeometry()) + mitkThrow() << "Geometry of input image is not an image geometry."; + } + + void mitk::CropTimestepsImageFilter::GenerateOutputInformation() + { + Image::ConstPointer input = this->GetInput(); + Image::Pointer output = this->GetOutput(); + if (m_LowerBoundaryTimestep > m_UpperBoundaryTimestep) { + mitkThrow() << "lower timestep is larger than upper timestep."; + } + if (m_UpperBoundaryTimestep == std::numeric_limits::max()) { + m_UpperBoundaryTimestep = input->GetTimeSteps(); + } + else if (m_UpperBoundaryTimestep > input->GetTimeSteps()) { + m_UpperBoundaryTimestep = input->GetTimeSteps(); + MITK_WARN << "upper boundary timestep set to " << m_UpperBoundaryTimestep; + } + m_DesiredRegion = ComputeDesiredRegion(); + unsigned int dimension = input->GetDimension(); + auto dimensions = new unsigned int[dimension]; + itk2vtk(m_DesiredRegion.GetSize(), dimensions); + if (dimension > 3) + memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int)); + + dimensions[3] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep; + + // create basic slicedGeometry that will be initialized below + output->Initialize(mitk::PixelType(input->GetPixelType()), dimension, dimensions); + delete[] dimensions; + auto newTimeGeometry = AdaptTimeGeometry(input->GetTimeGeometry(), m_LowerBoundaryTimestep, m_UpperBoundaryTimestep); + output->SetTimeGeometry(newTimeGeometry); + output->SetPropertyList(input->GetPropertyList()); + } + + mitk::SlicedData::RegionType mitk::CropTimestepsImageFilter::ComputeDesiredRegion() const + { + auto desiredRegion = this->GetInput()->GetLargestPossibleRegion(); + auto index = desiredRegion.GetIndex(); + auto size = desiredRegion.GetSize(); + unsigned int timeDimension = 3; + index[timeDimension] = m_LowerBoundaryTimestep; + size[timeDimension] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep; + desiredRegion.SetIndex(index); + desiredRegion.SetSize(size); + return desiredRegion; + } + + mitk::TimeGeometry::Pointer mitk::CropTimestepsImageFilter::AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const + { + auto newTimeGeometry = mitk::ArbitraryTimeGeometry::New(); + newTimeGeometry->ClearAllGeometries(); + for (unsigned int timestep = startTimestep; timestep < endTimestep; timestep++) { + auto geometryForTimePoint = sourceGeometry->GetGeometryForTimeStep(timestep); + newTimeGeometry->AppendNewTimeStep(geometryForTimePoint, + sourceGeometry->GetMinimumTimePoint(timestep), + sourceGeometry->GetMaximumTimePoint(timestep)); + } + return newTimeGeometry.GetPointer(); + } + +void mitk::CropTimestepsImageFilter::GenerateData() +{ + const auto* inputImage = this->GetInput(); + mitk::Image::Pointer output = this->GetOutput(); + + if ((output->IsInitialized() == false)) + return; + + auto timeSelector = mitk::ImageTimeSelector::New(); + + timeSelector->SetInput(inputImage); + + unsigned int timeStart = m_DesiredRegion.GetIndex(3); + unsigned int timeEnd = timeStart + m_DesiredRegion.GetSize(3); + for (unsigned int timestep = timeStart; timestep < timeEnd; ++timestep) + { + timeSelector->SetTimeNr(timestep); + timeSelector->UpdateLargestPossibleRegion(); + mitk::ImageReadAccessor imageAccessorWithOneTimestep(timeSelector->GetOutput()); + output->SetVolume(imageAccessorWithOneTimestep.GetData(), timestep-timeStart); + } +} + +void mitk::CropTimestepsImageFilter::SetInput(const InputImageType* image) +{ + if (this->GetInput() == image) + return; + + Superclass::SetInput(image); +} + +void mitk::CropTimestepsImageFilter::SetInput(unsigned int index, const InputImageType* image) +{ + if (0 != index) + mitkThrow() << "Input index " << index << " is invalid."; + + this->SetInput(image); +} + +void mitk::CropTimestepsImageFilter::VerifyInputInformation() +{ + Superclass::VerifyInputInformation(); + + VerifyInputImage(this->GetInput()); +}