diff --git a/CMakeExternals/MITKData.cmake b/CMakeExternals/MITKData.cmake index 6541bf6832..df23bbfb10 100644 --- a/CMakeExternals/MITKData.cmake +++ b/CMakeExternals/MITKData.cmake @@ -1,36 +1,36 @@ #----------------------------------------------------------------------------- # MITK Data #----------------------------------------------------------------------------- # Sanity checks if(DEFINED MITK_DATA_DIR AND NOT EXISTS ${MITK_DATA_DIR}) message(FATAL_ERROR "MITK_DATA_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MITK-Data) set(proj_DEPENDENCIES) set(MITK-Data_DEPENDS ${proj}) if(BUILD_TESTING) - set(revision_tag 2ffa73f0) # first 8 characters of hash-tag + set(revision_tag ec9cf10e) # first 8 characters of hash-tag # ^^^^^^^^ these are just to check correct length of hash part ExternalProject_Add(${proj} SOURCE_DIR ${proj} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MITK-Data_${revision_tag}.tar.gz UPDATE_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${proj_DEPENDENCIES} ) set(MITK_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif(BUILD_TESTING) diff --git a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp index af9ee64d88..4c2662f340 100644 --- a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp @@ -1,185 +1,240 @@ /*=================================================================== 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 "mitkMaskImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkTimeHelper.h" #include "mitkProperties.h" #include "mitkImageToItk.h" #include "mitkImageAccessByItk.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include mitk::MaskImageFilter::MaskImageFilter() : m_Mask(nullptr) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_MaskTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); m_OverrideOutsideValue = false; m_OutsideValue = 0; } mitk::MaskImageFilter::~MaskImageFilter() { } void mitk::MaskImageFilter::SetMask( const mitk::Image* mask ) { // Process object is not const-correct so the const_cast is required here m_Mask = const_cast< mitk::Image * >( mask ); this->ProcessObject::SetNthInput(1, m_Mask ); } const mitk::Image* mitk::MaskImageFilter::GetMask() const { return m_Mask; } void mitk::MaskImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image* output = this->GetOutput(); mitk::Image* input = const_cast< mitk::Image * > ( this->GetInput() ); mitk::Image* mask = m_Mask ; if((output->IsInitialized()==false) || (mask == nullptr) || (mask->GetTimeGeometry()->CountTimeSteps() == 0)) return; input->SetRequestedRegionToLargestPossibleRegion(); mask->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion(output, input); GenerateTimeInInputRegion(output, mask); } void mitk::MaskImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<<"GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } template < typename TPixel, unsigned int VImageDimension > void mitk::MaskImageFilter::InternalComputeMask(itk::Image* inputItkImage) { - typedef itk::Image ItkInputImageType; - typedef itk::Image ItkMaskImageType; - typedef itk::Image ItkOutputImageType; - - typedef itk::ImageRegionConstIterator< ItkInputImageType > ItkInputImageIteratorType; - typedef itk::ImageRegionConstIterator< ItkMaskImageType > ItkMaskImageIteratorType; - typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; - - typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); - maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); - maskimagetoitk->Update(); - typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); - - typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); - outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); - outputimagetoitk->Update(); - typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); - - // create the iterators - typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); - ItkInputImageIteratorType inputIt( inputItkImage, inputRegionOfInterest ); - ItkMaskImageIteratorType maskIt ( maskItkImage, inputRegionOfInterest ); - ItkOutputImageIteratorType outputIt( outputItkImage, inputRegionOfInterest ); - - //typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); - if ( !m_OverrideOutsideValue ) - m_OutsideValue = itk::NumericTraits::min(); - - m_MinValue = std::numeric_limits::max(); - m_MaxValue = std::numeric_limits::min(); - - for ( inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) + // dirty quick fix, duplicating code so both unsigned char and unsigned short are supported + // this should be changed once unsigned char segmentations can be converted to unsigned short + mitk::PixelType pixelType = m_MaskTimeSelector->GetOutput()->GetImageDescriptor()->GetChannelDescriptor().GetPixelType(); + if (pixelType.GetComponentType() == itk::ImageIOBase::UCHAR) { - if ( maskIt.Get() > itk::NumericTraits::Zero ) + typedef itk::Image ItkInputImageType; + typedef itk::Image ItkMaskImageType; + typedef itk::Image ItkOutputImageType; + + typedef itk::ImageRegionConstIterator< ItkInputImageType > ItkInputImageIteratorType; + typedef itk::ImageRegionConstIterator< ItkMaskImageType > ItkMaskImageIteratorType; + typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; + + typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); + maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); + maskimagetoitk->Update(); + typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); + + typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); + outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); + outputimagetoitk->Update(); + typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); + + // create the iterators + typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); + ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); + ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest); + ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); + + //typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); + if (!m_OverrideOutsideValue) + m_OutsideValue = itk::NumericTraits::min(); + + m_MinValue = std::numeric_limits::max(); + m_MaxValue = std::numeric_limits::min(); + + for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) { - outputIt.Set(inputIt.Get()); - m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); - m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); + if (maskIt.Get() > itk::NumericTraits::Zero) + { + outputIt.Set(inputIt.Get()); + m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); + m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); + } + else + { + outputIt.Set(m_OutsideValue); + } } - else + } + else + { { - outputIt.Set(m_OutsideValue); + typedef itk::Image ItkInputImageType; + typedef itk::Image ItkMaskImageType; + typedef itk::Image ItkOutputImageType; + + typedef itk::ImageRegionConstIterator< ItkInputImageType > ItkInputImageIteratorType; + typedef itk::ImageRegionConstIterator< ItkMaskImageType > ItkMaskImageIteratorType; + typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; + + typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); + maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); + maskimagetoitk->Update(); + typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); + + typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); + outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); + outputimagetoitk->Update(); + typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); + + // create the iterators + typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); + ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); + ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest); + ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); + + //typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); + if (!m_OverrideOutsideValue) + m_OutsideValue = itk::NumericTraits::min(); + + m_MinValue = std::numeric_limits::max(); + m_MaxValue = std::numeric_limits::min(); + + for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) + { + if (maskIt.Get() > itk::NumericTraits::Zero) + { + outputIt.Set(inputIt.Get()); + m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); + m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); + } + else + { + outputIt.Set(m_OutsideValue); + } + } } } } void mitk::MaskImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer mask = m_Mask; mitk::Image::Pointer output = this->GetOutput(); if((output->IsInitialized()==false) || (mask.IsNull()) || (mask->GetTimeGeometry()->CountTimeSteps() == 0)) return; m_InputTimeSelector->SetInput(input); m_MaskTimeSelector->SetInput(mask); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); const mitk::TimeGeometry *maskTimeGeometry = mask->GetTimeGeometry(); ScalarType timeInMS; int timestep=0; int tstart=outputRegion.GetIndex(3); int tmax=tstart+outputRegion.GetSize(3); int t; for(t=tstart;tTimeStepToTimePoint( t ); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); timestep = maskTimeGeometry->TimePointToTimeStep( timeInMS ); m_MaskTimeSelector->SetTimeNr(timestep); m_MaskTimeSelector->UpdateLargestPossibleRegion(); AccessByItk(m_InputTimeSelector->GetOutput(),InternalComputeMask); } m_TimeOfHeaderInitialization.Modified(); } diff --git a/Modules/Core/include/mitkSurfaceToImageFilter.h b/Modules/Core/include/mitkSurfaceToImageFilter.h index 2030534e46..b2ff5f83b2 100644 --- a/Modules/Core/include/mitkSurfaceToImageFilter.h +++ b/Modules/Core/include/mitkSurfaceToImageFilter.h @@ -1,98 +1,98 @@ /*=================================================================== 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 _mitkSurfaceToImageFilter_h__ #define _mitkSurfaceToImageFilter_h__ #include "mitkCommon.h" #include "MitkCoreExports.h" #include "mitkImageSource.h" #include "mitkSurface.h" -//#include "mitkImage.h" class vtkPolyData; namespace mitk { -//class Mesh; -//class VectorOfContourLines; - /** * * @brief Converts surface data to pixel data. Requires a surface and an * image, which header information defines the output image. * * The resulting image has the same dimension, size, and Geometry3D * as the input image. The image is cut using a vtkStencil. * The user can decide if he wants to keep the original values or create a * binary image by setting MakeBinaryOutputOn (default is \a false). If * set to \a true all voxels inside the surface are set to one and all * outside voxel are set to zero. * * NOTE: Since the reference input image is passed to the vtkStencil in * any case, the image needs to be initialized with pixel values greater than * the numerical minimum of the used pixel type (e.g. at least -127 for * unsigned char images, etc.) to produce a correct binary image * representation of the surface in MakeOutputBinary mode. * * @ingroup SurfaceFilters * @ingroup Process */ class MITKCORE_EXPORT SurfaceToImageFilter : public ImageSource { public: mitkClassMacro(SurfaceToImageFilter, ImageSource); itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkSetMacro(MakeOutputBinary, bool); itkGetMacro(MakeOutputBinary, bool); itkBooleanMacro(MakeOutputBinary); + itkSetMacro(UShortBinaryPixelType, bool); + itkGetMacro(UShortBinaryPixelType, bool); + itkBooleanMacro(UShortBinaryPixelType); + itkGetConstMacro(BackgroundValue,float); itkSetMacro(BackgroundValue,float); virtual void GenerateInputRequestedRegion() override; virtual void GenerateOutputInformation() override; virtual void GenerateData() override; const mitk::Surface *GetInput(void); using itk::ProcessObject::SetInput; virtual void SetInput(const mitk::Surface *surface); void SetImage(const mitk::Image *source); const mitk::Image *GetImage(void); protected: SurfaceToImageFilter(); virtual ~SurfaceToImageFilter(); void Stencil3DImage(int time = 0); bool m_MakeOutputBinary; + bool m_UShortBinaryPixelType; float m_BackgroundValue; - }; } // namespace mitk #endif /* MITKCOONSPATCHFILTER_H_HEADER_INCLUDED_C10B22CD */ diff --git a/Modules/Core/src/Algorithms/mitkSurfaceToImageFilter.cpp b/Modules/Core/src/Algorithms/mitkSurfaceToImageFilter.cpp index 9a0b10d348..4ca1e22b60 100644 --- a/Modules/Core/src/Algorithms/mitkSurfaceToImageFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkSurfaceToImageFilter.cpp @@ -1,221 +1,247 @@ /*=================================================================== 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 "mitkSurfaceToImageFilter.h" -#include "mitkTimeHelper.h" +#include #include "mitkImageWriteAccessor.h" +#include "mitkTimeHelper.h" -#include -#include -#include #include +#include +#include #include -#include -#include -#include -#include -#include -#include #include +#include +#include #include #include -#include -#include mitk::SurfaceToImageFilter::SurfaceToImageFilter() : m_MakeOutputBinary( false ), + m_UShortBinaryPixelType( false ), m_BackgroundValue( -10000 ) { } mitk::SurfaceToImageFilter::~SurfaceToImageFilter() { } void mitk::SurfaceToImageFilter::GenerateInputRequestedRegion() { mitk::Image* output = this->GetOutput(); if((output->IsInitialized()==false) ) return; GenerateTimeInInputRegion(output, const_cast< mitk::Image * > ( this->GetImage() )); } void mitk::SurfaceToImageFilter::GenerateOutputInformation() { mitk::Image *inputImage = (mitk::Image*)this->GetImage(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); if((inputImage == nullptr) || (inputImage->IsInitialized() == false) || (inputImage->GetTimeGeometry() == nullptr)) return; if (m_MakeOutputBinary) - output->Initialize(mitk::MakeScalarPixelType() , *inputImage->GetTimeGeometry()); + { + if (m_UShortBinaryPixelType) + { + output->Initialize(mitk::MakeScalarPixelType() , *inputImage->GetTimeGeometry()); + } + else + { + output->Initialize(mitk::MakeScalarPixelType() , *inputImage->GetTimeGeometry()); + } + } else + { output->Initialize(inputImage->GetPixelType(), *inputImage->GetTimeGeometry()); + } output->SetPropertyList(inputImage->GetPropertyList()->Clone()); } void mitk::SurfaceToImageFilter::GenerateData() { mitk::Image::ConstPointer inputImage = this->GetImage(); mitk::Image::Pointer output = this->GetOutput(); if(inputImage.IsNull()) return; if(output->IsInitialized()==false ) return; mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); int tstart=outputRegion.GetIndex(3); int tmax=tstart+outputRegion.GetSize(3); if ( tmax > 0) { int t; for(t=tstart;tGetOutput(); mitk::Image::Pointer binaryImage = mitk::Image::New(); unsigned int size = sizeof(unsigned char); if (m_MakeOutputBinary) - binaryImage->Initialize(mitk::MakeScalarPixelType(), *this->GetImage()->GetTimeGeometry(),1,1); + { + if (m_UShortBinaryPixelType) + { + binaryImage->Initialize(mitk::MakeScalarPixelType(), *this->GetImage()->GetTimeGeometry(),1,1); + size = sizeof(unsigned short); + } + else + { + binaryImage->Initialize(mitk::MakeScalarPixelType(), *this->GetImage()->GetTimeGeometry(),1,1); + } + } else { binaryImage->Initialize(this->GetImage()->GetPixelType(), *this->GetImage()->GetTimeGeometry(),1,1); size = this->GetImage()->GetPixelType().GetSize(); } for (unsigned int i = 0; i < binaryImage->GetDimension(); ++i) + { size *= binaryImage->GetDimension(i); + } mitk::ImageWriteAccessor accessor( binaryImage ); memset( accessor.GetData(), 1, size ); const mitk::TimeGeometry *surfaceTimeGeometry = GetInput()->GetTimeGeometry(); const mitk::TimeGeometry *imageTimeGeometry = GetImage()->GetTimeGeometry(); // Convert time step from image time-frame to surface time-frame mitk::TimePointType matchingTimePoint = imageTimeGeometry->TimeStepToTimePoint(time); mitk::TimeStepType surfaceTimeStep = surfaceTimeGeometry->TimePointToTimeStep(matchingTimePoint); vtkPolyData * polydata = ( (mitk::Surface*)GetInput() )->GetVtkPolyData( surfaceTimeStep ); if(polydata) { vtkSmartPointer move = vtkSmartPointer::New(); move->SetInputData(polydata); move->ReleaseDataFlagOn(); vtkSmartPointer transform = vtkSmartPointer::New(); BaseGeometry* geometry = surfaceTimeGeometry->GetGeometryForTimeStep( surfaceTimeStep ); if(!geometry) { geometry = GetInput()->GetGeometry(); } transform->PostMultiply(); transform->Concatenate(geometry->GetVtkTransform()->GetMatrix()); // take image geometry into account. vtk-Image information will be changed to unit spacing and zero origin below. BaseGeometry* imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time); transform->Concatenate(imageGeometry->GetVtkTransform()->GetLinearInverse()); move->SetTransform(transform); vtkSmartPointer normalsFilter = vtkSmartPointer::New(); normalsFilter->SetFeatureAngle(50); normalsFilter->SetConsistency(1); normalsFilter->SetSplitting(1); normalsFilter->SetFlipNormals(0); normalsFilter->ReleaseDataFlagOn(); normalsFilter->SetInputConnection(move->GetOutputPort()); vtkSmartPointer surfaceConverter = vtkSmartPointer::New(); surfaceConverter->SetTolerance( 0.0 ); surfaceConverter->ReleaseDataFlagOn(); surfaceConverter->SetInputConnection( normalsFilter->GetOutputPort() ); vtkImageData *image = m_MakeOutputBinary ? binaryImage->GetVtkImageData() : const_cast(this->GetImage())->GetVtkImageData(time); + // fill the image with foreground voxels: + unsigned char inval = 1; + vtkIdType count = image->GetNumberOfPoints(); + for (vtkIdType i = 0; i < count; ++i) + { + image->GetPointData()->GetScalars()->SetTuple1(i, inval); + } + // Create stencil and use numerical minimum of pixel type as background value vtkSmartPointer stencil = vtkSmartPointer::New(); stencil->SetInputData(image); stencil->ReverseStencilOff(); stencil->ReleaseDataFlagOn(); stencil->SetStencilConnection(surfaceConverter->GetOutputPort()); stencil->SetBackgroundValue(m_MakeOutputBinary ? 0 : m_BackgroundValue); stencil->Update(); output->SetVolume( stencil->GetOutput()->GetScalarPointer(), time ); MITK_INFO << "stencil ref count: " << stencil->GetReferenceCount() << std::endl; } else { memset( accessor.GetData(), 0, size ); output->SetVolume(accessor.GetData(),time); } } const mitk::Surface *mitk::SurfaceToImageFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast ( this->ProcessObject::GetInput(0) ); } void mitk::SurfaceToImageFilter::SetInput(const mitk::Surface *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast< mitk::Surface * >( input ) ); } void mitk::SurfaceToImageFilter::SetImage(const mitk::Image *source) { this->ProcessObject::SetNthInput( 1, const_cast< mitk::Image * >( source ) ); } const mitk::Image *mitk::SurfaceToImageFilter::GetImage(void) { return static_cast< const mitk::Image * >(this->ProcessObject::GetInput(1)); } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index ac31dc4cc2..2376813cb0 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,482 +1,481 @@ /*=================================================================== 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 "mitkLevelWindowManager.h" #include #include "mitkDataStorage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateNot.h" #include "mitkProperties.h" #include "mitkMessage.h" #include "mitkRenderingModeProperty.h" #include "mitkImage.h" mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(NULL) , m_LevelWindowProperty(NULL) , m_AutoTopMost(true) , m_IsObserverTagSet(false) , m_CurrentImage(NULL) , m_IsPropertyModifiedTagSet(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageAddedNode )); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageRemovedNode )); m_DataStorage = NULL; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } //clear both observer maps this->ClearPropObserverLists(); } void mitk::LevelWindowManager::SetDataStorage( mitk::DataStorage* ds ) { if (ds == NULL) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageAddedNode )); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageRemovedNode )); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1( this, &LevelWindowManager::DataStorageAddedNode )); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1( this, &LevelWindowManager::DataStorageRemovedNode )); this->DataStorageAddedNode(); // update us with new DataStorage } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject& ) { Modified(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const mitk::DataNode* removedNode) { m_AutoTopMost = autoTopMost; if (m_AutoTopMost == false) return; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } /* search topmost image */ if (m_DataStorage.IsNull()) { itkExceptionMacro("DataStorage not set"); } int maxLayer = itk::NumericTraits::min(); m_LevelWindowProperty = NULL; mitk::DataNode::Pointer topLevelNode; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull() || (removedNode != NULL && node == removedNode)) continue; node->SetBoolProperty( "imageForLevelWindow", false ); if (node->IsVisible(NULL) == false) continue; int layer = 0; node->GetIntProperty("layer", layer); if ( layer < maxLayer ) continue; mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; int nonLvlWinMode1 = mitk::RenderingModeProperty::LOOKUPTABLE_COLOR; int nonLvlWinMode2 = mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(node->GetProperty( "Image Rendering.Mode" )); if( mode.IsNotNull() ) { int currMode = mode->GetRenderingMode(); if ( currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2 ) { continue; } } else continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); topLevelNode = node; maxLayer = layer; } if (topLevelNode.IsNotNull()) { topLevelNode->SetBoolProperty( "imageForLevelWindow", true ); } this->SetLevelWindowProperty( m_LevelWindowProperty ); if ( m_LevelWindowProperty.IsNull() ) { Modified(); } // else SetLevelWindowProperty will call Modified(); } // sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) return; if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver( itk::ModifiedEvent(), command ); m_IsPropertyModifiedTagSet = true; /* search image than belongs to the property */ mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); mitk::DataNode* n = m_DataStorage->GetNode(p); if (n == NULL) { mitkThrow() << "No Image in DataStorage that belongs to LevelWindow property" << m_LevelWindowProperty; } m_CurrentImage = dynamic_cast(n->GetData()); n->SetBoolProperty( "imageForLevelWindow", true ); this->Modified(); } // returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } // returns Level/Window values for the current image const mitk::LevelWindow& mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { itkExceptionMacro("No LevelWindow available!"); } } // sets new Level/Window values and informs all listeners about changes void mitk::LevelWindowManager::SetLevelWindow(const mitk::LevelWindow& levelWindow) { if (m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } void mitk::LevelWindowManager::DataStorageAddedNode( const mitk::DataNode* ) { //update observers with new data storage UpdateObservers(); //Initialize LevelWindowsManager to new image SetAutoTopMostImage(true); //check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != this->GetRelevantNodes()->size())) {mitkThrow() << "Wrong number of observers in Level Window Manager!";} } void mitk::LevelWindowManager::DataStorageRemovedNode( const mitk::DataNode* removedNode ) { //first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change anything. if ((this->GetRelevantNodes()->size() == 0)) return; bool removedNodeIsRelevant = false; /* Iterator code: is crashing, don't know why... so using for loop for (mitk::DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); it != this->GetRelevantNodes()->End(); ++it) {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ for (unsigned int i=0; iGetRelevantNodes()->size(); i++) { if (this->GetRelevantNodes()->at(i) == removedNode) {removedNodeIsRelevant=true;} } if (!removedNodeIsRelevant) return; //remember node which will be removed m_NodeMarkedToDelete = removedNode; //update observers UpdateObservers(); /* search image than belongs to the property */ if (m_LevelWindowProperty.IsNull()) { SetAutoTopMostImage(true, removedNode); } else { mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); mitk::DataNode* n = m_DataStorage->GetNode(p2); if (n == NULL || m_AutoTopMost) // if node was deleted, change our behaviour to AutoTopMost, if AutoTopMost is true change level window to topmost node { SetAutoTopMostImage(true, removedNode); } } //reset variable m_NodeMarkedToDelete = NULL; //check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != (this->GetRelevantNodes()->size()-1))) {mitkThrow() << "Wrong number of observers in Level Window Manager!";} } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropObserverLists(); //remove old observers CreatePropObserverLists(); //create new observer lists } int mitk::LevelWindowManager::GetNumberOfObservers() { return m_PropObserverToNode.size(); } mitk::DataStorage* mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } // true if changes on slider or line-edits will affect always the topmost layer image bool mitk::LevelWindowManager::isAutoTopMost() { return m_AutoTopMost; } void mitk::LevelWindowManager::Update(const itk::EventObject&) // visible property of a image has changed { if (m_AutoTopMost) { SetAutoTopMostImage(true); return; } mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; node->SetBoolProperty( "imageForLevelWindow", false ); if (node->IsVisible(NULL) == false) continue; mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); node->SetBoolProperty( "imageForLevelWindow", true ); if (m_LevelWindowProperty.IsNull() && m_LevelWindowProperty.GetPointer() == levelWindowProperty) // we found our m_LevelWindowProperty { return; } } Modified(); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { if (m_DataStorage.IsNull()) return mitk::DataStorage::SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return empty set - mitk::BoolProperty::Pointer trueProp = mitk::BoolProperty::New(true); mitk::NodePredicateProperty::Pointer notBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(false)); mitk::NodePredicateProperty::Pointer hasLevelWindow = mitk::NodePredicateProperty::New("levelwindow", NULL); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isDImage = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isTImage = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isQImage = mitk::NodePredicateDataType::New("QBallImage"); mitk::NodePredicateOr::Pointer predicateTypes = mitk::NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isQImage); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); mitk::DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset( predicate ); return relevantNodes; } mitk::Image* mitk::LevelWindowManager::GetCurrentImage() { return m_CurrentImage; } void mitk::LevelWindowManager::ClearPropObserverLists() { for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode.clear(); for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode2.clear(); for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode3.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) //check if data storage is set {itkExceptionMacro("DataStorage not set");} /* add observers for all relevant nodes */ mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) {continue;} /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long idx = it->Value()->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ); m_PropObserverToNode[PropDataPair(idx, it->Value())] = it->Value()->GetProperty("visible"); } /* add observers for all layer properties*/ for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) {continue;} /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long idx = it->Value()->GetProperty("layer")->AddObserver( itk::ModifiedEvent(), command2 ); m_PropObserverToNode2[PropDataPair(idx, it->Value())] = it->Value()->GetProperty("layer"); } /* add observers for all Image rendering.mode properties*/ for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) {continue;} /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if( imageRenderingMode.IsNotNull() ) { unsigned long idx = imageRenderingMode->AddObserver( itk::ModifiedEvent(), command3 ); m_PropObserverToNode3[PropDataPair(idx, it->Value())] = imageRenderingMode.GetPointer(); } } } diff --git a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp index 1e406743e8..ea06b8abbc 100644 --- a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp +++ b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp @@ -1,172 +1,177 @@ /*=================================================================== 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 #include #include #include #include "mitkSurfaceToImageFilter.h" #include #include "mitkTestFixture.h" class mitkSurfaceToImageFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSurfaceToImageFilterTestSuite); MITK_TEST(test3DSurfaceValidOutput); MITK_TEST(test3DSurfaceCorrect); MITK_TEST(test3DSurfaceIn4DImage); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Surface::Pointer m_Surface; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Surface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("ball.stl")); } void tearDown() override { } void test3DSurfaceValidOutput() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); additionalInputImage->Initialize( mitk::MakeScalarPixelType(), *m_Surface->GetTimeGeometry()); //Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + + surfaceToImageFilter->SetUShortBinaryPixelType(true); + surfaceToImageFilter->Update(); + + CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT); } void test3DSurfaceCorrect() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); //todo I don't know if this image is always needed. There is no documentation of the filter. Use git blame and ask the author. mitk::Image::Pointer additionalInputImage = mitk::Image::New(); unsigned int* dims = new unsigned int[3]; dims[0] = 32; dims[1] = 32; dims[2] = 32; additionalInputImage->Initialize( mitk::MakeScalarPixelType(),3,dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); //Arrange the filter //The docu does not really tell if this is always needed. Could we skip SetImage in any case? surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<3> idx; bool valuesCorrect = true; //Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); //Values inside the ball should be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_OutputCorrect", valuesCorrect == true); } void test3DSurfaceIn4DImage() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); unsigned int* dims = new unsigned int[4]; dims[0] = 32; dims[1] = 32; dims[2] = 32; dims[3] = 2; additionalInputImage->Initialize( mitk::MakeScalarPixelType(),4,dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); mitk::Image::Pointer secondStep = additionalInputImage->Clone(); unsigned int size = sizeof(unsigned char); for (unsigned int i = 0; i < secondStep->GetDimension(); ++i) size *= secondStep->GetDimension(i); mitk::ImageWriteAccessor accessor( secondStep ); memset( accessor.GetData(), 1, size ); additionalInputImage->GetTimeGeometry()->Expand(2); additionalInputImage->GetGeometry(1)->SetSpacing(secondStep->GetGeometry()->GetSpacing()); additionalInputImage->GetGeometry(1)->SetOrigin(secondStep->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry(1)->SetIndexToWorldTransform(secondStep->GetGeometry()->GetIndexToWorldTransform()); additionalInputImage->SetImportVolume(secondStep->GetData(),0); additionalInputImage->SetImportVolume(secondStep->GetData(),1); //Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<4> idx; bool valuesCorrect = true; //Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); //Values inside the ball should be 1 hould be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); //Values inside the ball but in the second timestep hould be 0 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_Output4DCorrect", valuesCorrect == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSurfaceToImageFilter) diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake index 2337956310..61b61ab602 100644 --- a/Modules/Multilabel/files.cmake +++ b/Modules/Multilabel/files.cmake @@ -1,17 +1,18 @@ set(CPP_FILES mitkLabel.cpp mitkLabelSet.cpp mitkLabelSetImage.cpp + mitkLabelSetImageConverter.cpp mitkLabelSetImageIO.cpp mitkLabelSetImageSerializer.cpp mitkLabelSetImageSource.cpp mitkLabelSetImageToSurfaceFilter.cpp mitkLabelSetImageToSurfaceThreadedFilter.cpp mitkLabelSetImageVtkMapper2D.cpp mitkMultilabelActivator.cpp mitkMultilabelObjectFactory.cpp ) set(RESOURCE_FILES ) diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp index 0316364dc7..2d2123b38b 100644 --- a/Modules/Multilabel/mitkLabelSetImage.cpp +++ b/Modules/Multilabel/mitkLabelSetImage.cpp @@ -1,1065 +1,985 @@ /*=================================================================== 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 "mitkLabelSetImage.h" #include "mitkImageAccessByItk.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkLookupTableProperty.h" #include "mitkPadImageFilter.h" #include #include #include #include #include #include //#include #include mitk::LabelSetImage::LabelSetImage() : mitk::Image(), m_ActiveLayer(0), m_ExteriorLabel(nullptr) { // Iniitlaize Background Label mitk::Color color; color.Set(0, 0, 0); m_ExteriorLabel = mitk::Label::New(); m_ExteriorLabel->SetColor(color); m_ExteriorLabel->SetName("Exterior"); m_ExteriorLabel->SetOpacity(0.0); m_ExteriorLabel->SetLocked(false); m_ExteriorLabel->SetValue(0); } mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage & other) : Image(other), m_ActiveLayer(other.GetActiveLayer()), m_ExteriorLabel(other.GetExteriorLabel()->Clone()) { for (unsigned int i = 0; i < other.GetNumberOfLayers(); i++) { // Clone LabelSet data mitk::LabelSet::Pointer lsClone = other.GetLabelSet(i)->Clone(); // add modified event listener to LabelSet (listen to LabelSet changes) itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified); lsClone->AddObserver(itk::ModifiedEvent(), command); m_LabelSetContainer.push_back(lsClone); // clone layer Image data mitk::Image::Pointer liClone = other.GetLayerImage(i)->Clone(); m_LayerContainer.push_back(liClone); } } void mitk::LabelSetImage::OnLabelSetModified() { Superclass::Modified(); } void mitk::LabelSetImage::SetExteriorLabel(mitk::Label * label) { m_ExteriorLabel = label; } mitk::Label* mitk::LabelSetImage::GetExteriorLabel() { return m_ExteriorLabel; } const mitk::Label* mitk::LabelSetImage::GetExteriorLabel() const { return m_ExteriorLabel; } void mitk::LabelSetImage::Initialize(const mitk::Image* other) { mitk::PixelType pixelType(mitk::MakeScalarPixelType()); if (other->GetDimension() == 2) { const unsigned int dimensions[] = { other->GetDimension(0), other->GetDimension(1), 1 }; Superclass::Initialize(pixelType, 3, dimensions); } else { Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions()); } mitk::TimeGeometry::Pointer originalGeometry = other->GetTimeGeometry()->Clone(); this->SetTimeGeometry(originalGeometry); // Add a inital LabelSet ans corresponding image data to the stack AddLayer(); } mitk::LabelSetImage::~LabelSetImage() { m_LabelSetContainer.clear(); } mitk::Image* mitk::LabelSetImage::GetLayerImage(unsigned int layer) { return m_LayerContainer[layer]; } const mitk::Image* mitk::LabelSetImage::GetLayerImage(unsigned int layer) const { return m_LayerContainer[layer]; } unsigned int mitk::LabelSetImage::GetActiveLayer() const { return m_ActiveLayer; } unsigned int mitk::LabelSetImage::GetNumberOfLayers() const { return m_LabelSetContainer.size(); } -mitk::LabelSetImage::VectorImageType::Pointer mitk::LabelSetImage::GetVectorImage(bool forceUpdate) const -{ - try - { - if (forceUpdate) - { - AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer()); - } - - LabelSetImageType::Pointer itkImage; - mitk::CastToItkImage(this, itkImage); - - VectorImageType::Pointer newVectorImage = VectorImageType::New(); - newVectorImage->SetSpacing(itkImage->GetSpacing()); - newVectorImage->SetOrigin(itkImage->GetOrigin()); - newVectorImage->SetDirection(itkImage->GetDirection()); - newVectorImage->SetRegions(itkImage->GetLargestPossibleRegion()); - newVectorImage->SetNumberOfComponentsPerPixel(m_LayerContainer.size()); - newVectorImage->Allocate(); - - itkImage = nullptr; - - // fill inside - VariableVectorType defaultValue; - defaultValue.SetSize(m_LayerContainer.size()); - defaultValue.Fill(0); - newVectorImage->FillBuffer(defaultValue); - - ImageAdaptorType::Pointer targetAdaptor = ImageAdaptorType::New(); - targetAdaptor->SetImage(newVectorImage); - - // transfer layers to a vector image. - for (unsigned int lidx = 0; lidx < m_LayerContainer.size(); ++lidx) - { - MITK_INFO << lidx; - LabelSetImageType::Pointer itkImageLayer; - mitk::CastToItkImage(m_LayerContainer[lidx], itkImageLayer); - - targetAdaptor->SetExtractComponentIndex(lidx); - targetAdaptor->Update(); - - typedef itk::ImageRegionConstIterator< LabelSetImageType > SourceIteratorType; - typedef itk::ImageRegionIterator< ImageAdaptorType > TargetIteratorType; - - SourceIteratorType sourceIter(itkImageLayer, itkImageLayer->GetLargestPossibleRegion()); - sourceIter.GoToBegin(); - - TargetIteratorType targetIter(targetAdaptor, targetAdaptor->GetLargestPossibleRegion()); - targetIter.GoToBegin(); - - while (!sourceIter.IsAtEnd()) - { - targetIter.Set(sourceIter.Get()); - ++sourceIter; - ++targetIter; - } - } - return newVectorImage; - } - catch (itk::ExceptionObject& e) - { - mitkThrow() << e.GetDescription(); - return nullptr; - } -} - -void mitk::LabelSetImage::SetVectorImage(VectorImageType::Pointer vectorImage) -{ - try - { - int numberOfLayers = vectorImage->GetNumberOfComponentsPerPixel(); - - MITK_INFO << numberOfLayers; - - ImageAdaptorType::Pointer vectorAdaptor = ImageAdaptorType::New(); - vectorAdaptor->SetImage(vectorImage); - - for (int lidx = 0; lidx < numberOfLayers; ++lidx) - { - LabelSetImageType::Pointer auxImg = LabelSetImageType::New(); - auxImg->SetSpacing(vectorImage->GetSpacing()); - auxImg->SetOrigin(vectorImage->GetOrigin()); - auxImg->SetDirection(vectorImage->GetDirection()); - auxImg->SetRegions(vectorImage->GetLargestPossibleRegion()); - auxImg->Allocate(); - - vectorAdaptor->SetExtractComponentIndex(lidx); - - typedef itk::ImageRegionConstIterator< ImageAdaptorType > SourceIteratorType; - typedef itk::ImageRegionIterator< LabelSetImageType > TargetIteratorType; - - SourceIteratorType sourceIter(vectorAdaptor, vectorAdaptor->GetLargestPossibleRegion()); - sourceIter.GoToBegin(); - - TargetIteratorType targetIter(auxImg, auxImg->GetLargestPossibleRegion()); - targetIter.GoToBegin(); - - while (!sourceIter.IsAtEnd()) - { - targetIter.Set(sourceIter.Get()); - ++sourceIter; - ++targetIter; - } - - mitk::Image::Pointer newLayerImage = mitk::Image::New(); - newLayerImage->Initialize(this); - newLayerImage->SetVolume(auxImg->GetBufferPointer()); - m_LayerContainer[lidx] = newLayerImage; - AccessByItk_1(this, LayerContainerToImageProcessing, lidx); - } - // reset active layer - SetActiveLayer(0); - } - catch (itk::ExceptionObject& e) - { - mitkThrow() << e.GetDescription(); - } - this->Modified(); -} - void mitk::LabelSetImage::RemoveLayer() { int layerToDelete = GetActiveLayer(); // remove all observers from active label set GetLabelSet(layerToDelete)->RemoveAllObservers(); - // set the active layer to one belo, if exists. - SetActiveLayer(layerToDelete - 1); + // set the active layer to one below, if exists. + SetActiveLayer(layerToDelete-1); // remove labelset and image data m_LabelSetContainer.erase(m_LabelSetContainer.begin() + layerToDelete); m_LayerContainer.erase(m_LayerContainer.begin() + layerToDelete); this->Modified(); } +template +void SetToZero(itk::Image< TPixel, VDimensions> * source) +{ + source->FillBuffer(0); +} + unsigned int mitk::LabelSetImage::AddLayer(mitk::LabelSet::Pointer lset) { mitk::Image::Pointer newImage = mitk::Image::New(); - newImage->Initialize(this); + newImage->Initialize( this->GetPixelType(), this->GetDimension(), this->GetDimensions(), this->GetImageDescriptor()->GetNumberOfChannels() ); + newImage->SetGeometry(this->GetGeometry()->Clone()); - LabelSetImageType::Pointer itkImage; - mitk::CastToItkImage(newImage, itkImage); - itkImage->FillBuffer(0); + if (newImage->GetDimension() < 4) + { + AccessByItk(newImage, SetToZero); + } + else + { + AccessFixedDimensionByItk(newImage, SetToZero, 4); + } + + unsigned int newLabelSetId = this->AddLayer(newImage, lset); - // get upcoming new labelset id + return newLabelSetId; +} + +unsigned int mitk::LabelSetImage::AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer lset) +{ unsigned int newLabelSetId = m_LayerContainer.size(); // Add labelset to layer mitk::LabelSet::Pointer ls; if (lset.IsNotNull()) { ls = lset; } else { ls = mitk::LabelSet::New(); ls->AddLabel(GetExteriorLabel()); ls->SetActiveLabel(0 /*Exterior Label*/); } ls->SetLayer(newLabelSetId); // Add exterior Label to label set //mitk::Label::Pointer exteriorLabel = CreateExteriorLabel(); // push a new working image for the new layer - m_LayerContainer.push_back(newImage); + m_LayerContainer.push_back(layerImage); // push a new labelset for the new layer m_LabelSetContainer.push_back(ls); // add modified event listener to LabelSet (listen to LabelSet changes) itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified); ls->AddObserver(itk::ModifiedEvent(), command); - // important to release the itk image - itkImage = nullptr; - SetActiveLayer(newLabelSetId); //MITK_INFO << GetActiveLayer(); this->Modified(); return newLabelSetId; } +void mitk::LabelSetImage::AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet) +{ + if (m_LayerContainer.size() <= layerIdx) + { + mitkThrow() << "Trying to add labelSet to non-existing layer."; + } + + if (layerIdx < m_LabelSetContainer.size()) + { + m_LabelSetContainer[layerIdx] = labelSet; + } + else + { + while (layerIdx >= m_LabelSetContainer.size()) + { + mitk::LabelSet::Pointer defaultLabelSet = mitk::LabelSet::New(); + defaultLabelSet->AddLabel(GetExteriorLabel()); + defaultLabelSet->SetActiveLabel(0 /*Exterior Label*/); + defaultLabelSet->SetLayer(m_LabelSetContainer.size()); + m_LabelSetContainer.push_back(defaultLabelSet); + } + m_LabelSetContainer.push_back(labelSet); + } +} + void mitk::LabelSetImage::SetActiveLayer(unsigned int layer) { try { if ((layer != GetActiveLayer()) && (layer < this->GetNumberOfLayers())) { BeforeChangeLayerEvent.Send(); AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer()); m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer()); AfterchangeLayerEvent.Send(); } } catch (itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImage::Concatenate(mitk::LabelSetImage* other) { const unsigned int* otherDims = other->GetDimensions(); const unsigned int* thisDims = this->GetDimensions(); if ((otherDims[0] != thisDims[0]) || (otherDims[1] != thisDims[1]) || (otherDims[2] != thisDims[2])) mitkThrow() << "Dimensions do not match."; try { int numberOfLayers = other->GetNumberOfLayers(); for (int layer = 0; layer < numberOfLayers; ++layer) { this->SetActiveLayer(layer); AccessByItk_1(this, ConcatenateProcessing, other); mitk::LabelSet * ls = other->GetLabelSet(layer); auto it = ls->IteratorConstBegin(); auto end = ls->IteratorConstEnd(); it++;// skip exterior while (it != end) { GetLabelSet()->AddLabel((it->second)); //AddLabelEvent.Send(); it++; } } } catch (itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImage::ClearBuffer() { try { AccessByItk(this, ClearBufferProcessing); this->Modified(); } catch (itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } } bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue) const { bool exist = false; for (unsigned int lidx = 0; lidx < GetNumberOfLayers(); lidx++) exist |= m_LabelSetContainer[lidx]->ExistLabel(pixelValue); return exist; } bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue, unsigned int layer) const { bool exist = m_LabelSetContainer[layer]->ExistLabel(pixelValue); return exist; } bool mitk::LabelSetImage::ExistLabelSet(unsigned int layer) const { return layer < m_LabelSetContainer.size(); } void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, unsigned int /*layer*/) { int targetPixelValue = GetActiveLabel()->GetValue(); try { AccessByItk_2(this, MergeLabelProcessing, targetPixelValue, pixelValue); } catch (itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } Modified(); } void mitk::LabelSetImage::MergeLabels(std::vector &VectorOfLablePixelValues, PixelType pixelValue, unsigned int layer) { GetLabelSet(layer)->SetActiveLabel(pixelValue); try { for (unsigned int idx = 0; idx < VectorOfLablePixelValues.size(); idx++) { AccessByItk_2(this, MergeLabelProcessing, pixelValue, VectorOfLablePixelValues[idx]); } } catch (itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } } void mitk::LabelSetImage::RemoveLabels(std::vector& VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++) { GetLabelSet(layer)->RemoveLabel(VectorOfLabelPixelValues[idx]); EraseLabel(VectorOfLabelPixelValues[idx], layer); } } void mitk::LabelSetImage::EraseLabels(std::vector &VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int i = 0; i < VectorOfLabelPixelValues.size(); i++) { this->EraseLabel(VectorOfLabelPixelValues[i], layer); } } void mitk::LabelSetImage::EraseLabel(PixelType pixelValue, unsigned int layer) { try { AccessByItk_2(this, EraseLabelProcessing, pixelValue, layer); } catch (itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } Modified(); } mitk::Label *mitk::LabelSetImage::GetActiveLabel(unsigned int layer) { if (m_LabelSetContainer.size() > layer) return m_LabelSetContainer[layer]->GetActiveLabel(); else return nullptr; } mitk::Label *mitk::LabelSetImage::GetLabel(PixelType pixelValue, unsigned int layer) const { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer]->GetLabel(pixelValue); } mitk::LabelSet* mitk::LabelSetImage::GetLabelSet(unsigned int layer) { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer].GetPointer(); } const mitk::LabelSet* mitk::LabelSetImage::GetLabelSet(unsigned int layer) const { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer].GetPointer(); } mitk::LabelSet* mitk::LabelSetImage::GetActiveLabelSet() { if (m_LabelSetContainer.size() == 0) return nullptr; else return m_LabelSetContainer[GetActiveLayer()].GetPointer(); } void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue, unsigned int layer) { AccessByItk_2(this, CalculateCenterOfMassProcessing, pixelValue, layer); } unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const { return m_LabelSetContainer[layer]->GetNumberOfLabels(); } unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const { unsigned int totalLabels(0); auto layerIter = m_LabelSetContainer.begin(); for (; layerIter != m_LabelSetContainer.end(); ++layerIter) totalLabels += (*layerIter)->GetNumberOfLabels(); return totalLabels; } void mitk::LabelSetImage::MaskStamp(mitk::Image* mask, bool forceOverwrite) { try { mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New(); padImageFilter->SetInput(0, mask); padImageFilter->SetInput(1, this); padImageFilter->SetPadConstant(0); padImageFilter->SetBinaryFilter(false); padImageFilter->SetLowerThreshold(0); padImageFilter->SetUpperThreshold(1); padImageFilter->Update(); mitk::Image::Pointer paddedMask = padImageFilter->GetOutput(); if (paddedMask.IsNull()) return; AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite); } catch (...) { mitkThrow() << "Could not stamp the provided mask on the selected label."; } } mitk::Image::Pointer mitk::LabelSetImage::CreateLabelMask(PixelType index) { mitk::Image::Pointer mask = mitk::Image::New(); try { mask->Initialize(this); unsigned int byteSize = sizeof(LabelSetImage::PixelType); for (unsigned int dim = 0; dim < mask->GetDimension(); ++dim) { byteSize *= mask->GetDimension(dim); } mitk::ImageWriteAccessor* accessor = new mitk::ImageWriteAccessor(static_cast(mask)); memset(accessor->GetData(), 0, byteSize); delete accessor; mitk::SlicedGeometry3D::Pointer geometry = this->GetSlicedGeometry()->Clone(); mask->SetGeometry(geometry); AccessByItk_2(this, CreateLabelMaskProcessing, mask, index); } catch (...) { mitkThrow() << "Could not create a mask out of the selected label."; } return mask; } -void mitk::LabelSetImage::SurfaceStamp(mitk::Surface* surface, bool forceOverwrite) -{ - if (!surface) - { - MITK_ERROR << "Input surface is NULL."; - return; - } - - try - { - LabelSetImageType::Pointer itkImage; - mitk::CastToItkImage(this, itkImage); - - vtkPolyData *polydata = surface->GetVtkPolyData(); - - vtkSmartPointer transform = vtkSmartPointer::New(); - transform->SetMatrix(surface->GetGeometry()->GetVtkTransform()->GetMatrix()); - transform->Update(); - - vtkSmartPointer transformer = vtkSmartPointer::New(); - transformer->SetInputData(polydata); - transformer->SetTransform(transform); - transformer->Update(); - - typedef double Coord; - typedef itk::QuadEdgeMesh< Coord, 3 > MeshType; - - MeshType::Pointer mesh = MeshType::New(); - mesh->SetCellsAllocationMethod(MeshType::CellsAllocatedDynamicallyCellByCell); - int numberOfPoints = polydata->GetNumberOfPoints(); - mesh->GetPoints()->Reserve(numberOfPoints); - - vtkPoints* points = polydata->GetPoints(); - - MeshType::PointType point; - for (int i = 0; i < numberOfPoints; i++) - { - double* aux = points->GetPoint(i); - point[0] = aux[0]; - point[1] = aux[1]; - point[2] = aux[2]; - mesh->SetPoint(i, point); - } - - // Load the polygons into the itk::Mesh - typedef MeshType::CellAutoPointer CellAutoPointerType; - typedef MeshType::CellType CellType; - typedef itk::TriangleCell< CellType > TriangleCellType; - typedef MeshType::PointIdentifier PointIdentifierType; - typedef MeshType::CellIdentifier CellIdentifierType; - - // Read the number of polygons - CellIdentifierType numberOfPolygons = 0; - numberOfPolygons = polydata->GetNumberOfPolys(); - - PointIdentifierType numberOfCellPoints = 3; - - for (CellIdentifierType i = 0; i < numberOfPolygons; i++) - { - vtkIdList *cellIds; - vtkCell *vcell = polydata->GetCell(i); - cellIds = vcell->GetPointIds(); - - CellAutoPointerType cell; - auto triangleCell = new TriangleCellType; - PointIdentifierType k; - for (k = 0; k < numberOfCellPoints; k++) - { - triangleCell->SetPointId(k, cellIds->GetId(k)); - } - - cell.TakeOwnership(triangleCell); - mesh->SetCell(i, cell); - } - - typedef itk::TriangleMeshToBinaryImageFilter TriangleMeshToBinaryImageFilterType; - - TriangleMeshToBinaryImageFilterType::Pointer filter = TriangleMeshToBinaryImageFilterType::New(); - filter->SetInput(mesh); - filter->SetInfoImage(itkImage); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->Update(); - - LabelSetImageType::Pointer resultImage = filter->GetOutput(); - resultImage->DisconnectPipeline(); - - typedef itk::ImageRegionConstIterator< LabelSetImageType > SourceIteratorType; - typedef itk::ImageRegionIterator< LabelSetImageType > TargetIteratorType; - - SourceIteratorType sourceIter(resultImage, resultImage->GetLargestPossibleRegion()); - sourceIter.GoToBegin(); - - TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion()); - targetIter.GoToBegin(); - - int activeLabel = GetActiveLabel(GetActiveLayer())->GetValue(); - - while (!sourceIter.IsAtEnd()) - { - int sourceValue = static_cast(sourceIter.Get()); - int targetValue = static_cast(targetIter.Get()); - - if ((sourceValue != 0) && (forceOverwrite || !this->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels - { - targetIter.Set(activeLabel); - } - ++sourceIter; - ++targetIter; - } - } - catch (itk::ExceptionObject& e) - { - mitkThrow() << e.GetDescription(); - } - this->Modified(); -} +//void mitk::LabelSetImage::SurfaceStamp(mitk::Surface* surface, bool forceOverwrite) +//{ +// if (!surface) +// { +// MITK_ERROR << "Input surface is NULL."; +// return; +// } + +// try +// { +// LabelSetImageType::Pointer itkImage; +// mitk::CastToItkImage(this, itkImage); + +// vtkPolyData *polydata = surface->GetVtkPolyData(); + +// vtkSmartPointer transform = vtkSmartPointer::New(); +// transform->SetMatrix(surface->GetGeometry()->GetVtkTransform()->GetMatrix()); +// transform->Update(); + +// vtkSmartPointer transformer = vtkSmartPointer::New(); +// transformer->SetInputData(polydata); +// transformer->SetTransform(transform); +// transformer->Update(); + +// typedef double Coord; +// typedef itk::QuadEdgeMesh< Coord, 3 > MeshType; + +// MeshType::Pointer mesh = MeshType::New(); +// mesh->SetCellsAllocationMethod(MeshType::CellsAllocatedDynamicallyCellByCell); +// int numberOfPoints = polydata->GetNumberOfPoints(); +// mesh->GetPoints()->Reserve(numberOfPoints); + +// vtkPoints* points = polydata->GetPoints(); + +// MeshType::PointType point; +// for (int i = 0; i < numberOfPoints; i++) +// { +// double* aux = points->GetPoint(i); +// point[0] = aux[0]; +// point[1] = aux[1]; +// point[2] = aux[2]; +// mesh->SetPoint(i, point); +// } + +// // Load the polygons into the itk::Mesh +// typedef MeshType::CellAutoPointer CellAutoPointerType; +// typedef MeshType::CellType CellType; +// typedef itk::TriangleCell< CellType > TriangleCellType; +// typedef MeshType::PointIdentifier PointIdentifierType; +// typedef MeshType::CellIdentifier CellIdentifierType; + +// // Read the number of polygons +// CellIdentifierType numberOfPolygons = 0; +// numberOfPolygons = polydata->GetNumberOfPolys(); + +// PointIdentifierType numberOfCellPoints = 3; + +// for (CellIdentifierType i = 0; i < numberOfPolygons; i++) +// { +// vtkIdList *cellIds; +// vtkCell *vcell = polydata->GetCell(i); +// cellIds = vcell->GetPointIds(); + +// CellAutoPointerType cell; +// auto triangleCell = new TriangleCellType; +// PointIdentifierType k; +// for (k = 0; k < numberOfCellPoints; k++) +// { +// triangleCell->SetPointId(k, cellIds->GetId(k)); +// } + +// cell.TakeOwnership(triangleCell); +// mesh->SetCell(i, cell); +// } + +// typedef itk::TriangleMeshToBinaryImageFilter TriangleMeshToBinaryImageFilterType; + +// TriangleMeshToBinaryImageFilterType::Pointer filter = TriangleMeshToBinaryImageFilterType::New(); +// filter->SetInput(mesh); +// filter->SetInfoImage(itkImage); +// filter->SetInsideValue(1); +// filter->SetOutsideValue(0); +// filter->Update(); + +// LabelSetImageType::Pointer resultImage = filter->GetOutput(); +// resultImage->DisconnectPipeline(); + +// typedef itk::ImageRegionConstIterator< LabelSetImageType > SourceIteratorType; +// typedef itk::ImageRegionIterator< LabelSetImageType > TargetIteratorType; + +// SourceIteratorType sourceIter(resultImage, resultImage->GetLargestPossibleRegion()); +// sourceIter.GoToBegin(); + +// TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion()); +// targetIter.GoToBegin(); + +// int activeLabel = GetActiveLabel(GetActiveLayer())->GetValue(); + +// while (!sourceIter.IsAtEnd()) +// { +// int sourceValue = static_cast(sourceIter.Get()); +// int targetValue = static_cast(targetIter.Get()); + +// if ((sourceValue != 0) && (forceOverwrite || !this->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels +// { +// targetIter.Set(activeLabel); +// } +// ++sourceIter; +// ++targetIter; +// } +// } +// catch (itk::ExceptionObject& e) +// { +// mitkThrow() << e.GetDescription(); +// } +// this->Modified(); +//} void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image) { if (image.IsNull() || image->IsEmpty() || !image->IsInitialized()) mitkThrow() << "Invalid labeled image."; try { this->Initialize(image); unsigned int byteSize = sizeof(LabelSetImage::PixelType); for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { byteSize *= image->GetDimension(dim); } mitk::ImageWriteAccessor* accessor = new mitk::ImageWriteAccessor(static_cast(this)); memset(accessor->GetData(), 0, byteSize); delete accessor; mitk::SlicedGeometry3D::Pointer geometry = image->GetSlicedGeometry()->Clone(); this->SetGeometry(geometry); AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3); } catch (...) { mitkThrow() << "Could not intialize by provided labeled image."; } this->Modified(); } template < typename ImageType1, typename ImageType2 > void mitk::LabelSetImage::InitializeByLabeledImageProcessing(ImageType1* output, ImageType2* input) { typedef itk::ImageRegionConstIterator< ImageType2 > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType1 > TargetIteratorType; // typedef itk::RelabelComponentImageFilter FilterType; TargetIteratorType targetIter(output, output->GetLargestPossibleRegion()); targetIter.GoToBegin(); //SourceIteratorType sourceIter( relabelFilter->GetOutput(), relabelFilter->GetOutput()->GetLargestPossibleRegion() ); SourceIteratorType sourceIter(input, input->GetLargestPossibleRegion()); sourceIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { PixelType sourceValue = static_cast(sourceIter.Get()); targetIter.Set(sourceValue); if (!ExistLabel(sourceValue)) { std::stringstream name; name << "object-" << sourceValue; mitk::Label::Pointer label = mitk::Label::New(); label->SetName(name.str().c_str()); double rgba[4]; m_LabelSetContainer[GetActiveLayer()]->GetLookupTable()->GetTableValue(sourceValue, rgba); mitk::Color newColor; newColor.SetRed(rgba[0]); newColor.SetGreen(rgba[1]); newColor.SetBlue(rgba[2]); label->SetColor(newColor); label->SetOpacity(rgba[3]); label->SetValue(sourceValue); GetLabelSet()->AddLabel(label); if (GetActiveLabelSet()->GetNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE || sourceValue >= mitk::Label::MAX_LABEL_VALUE) { AddLayer(); } } ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::MaskStampProcessing(ImageType* itkImage, mitk::Image* mask, bool forceOverwrite) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter(itkMask, itkMask->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion()); targetIter.GoToBegin(); int activeLabel = this->GetActiveLabel(GetActiveLayer())->GetValue(); while (!sourceIter.IsAtEnd()) { PixelType sourceValue = sourceIter.Get(); PixelType targetValue = targetIter.Get(); if ((sourceValue != 0) && (forceOverwrite || !this->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels { targetIter.Set(activeLabel); } ++sourceIter; ++targetIter; } this->Modified(); } template < typename ImageType > void mitk::LabelSetImage::CreateLabelMaskProcessing(ImageType* itkImage, mitk::Image* mask, PixelType index) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter(itkImage, itkImage->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(itkMask, itkMask->GetLargestPossibleRegion()); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { PixelType sourceValue = sourceIter.Get(); if (sourceValue == index) { targetIter.Set(1); } ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType* itkImage, PixelType pixelValue, unsigned int layer) { // for now, we just retrieve the voxel in the middle typedef itk::ImageRegionConstIterator< ImageType > IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); std::vector< typename ImageType::IndexType > indexVector; while (!iter.IsAtEnd()) { // TODO fix comparison warning more effective if (iter.Get() == pixelValue) { indexVector.push_back(iter.GetIndex()); } ++iter; } mitk::Point3D pos; pos.Fill(0.0); if (!indexVector.empty()) { typename itk::ImageRegionConstIteratorWithIndex< ImageType >::IndexType centerIndex; centerIndex = indexVector.at(indexVector.size() / 2); if (centerIndex.GetIndexDimension() == 3) { pos[0] = centerIndex[0]; pos[1] = centerIndex[1]; pos[2] = centerIndex[2]; } else return; } GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassIndex(pos); this->GetSlicedGeometry()->IndexToWorld(pos, pos); GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos); } template < typename ImageType > void mitk::LabelSetImage::ClearBufferProcessing(ImageType* itkImage) { itkImage->FillBuffer(0); } // todo: concatenate all layers and not just the active one template < typename ImageType > void mitk::LabelSetImage::ConcatenateProcessing(ImageType* itkTarget, mitk::LabelSetImage* other) { typename ImageType::Pointer itkSource = ImageType::New(); mitk::CastToItkImage(other, itkSource); typedef itk::ImageRegionConstIterator< ImageType > ConstIteratorType; typedef itk::ImageRegionIterator< ImageType > IteratorType; ConstIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion()); IteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion()); int numberOfTargetLabels = this->GetNumberOfLabels(GetActiveLayer()) - 1; // skip exterior sourceIter.GoToBegin(); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { PixelType sourceValue = sourceIter.Get(); PixelType targetValue = targetIter.Get(); if ((sourceValue != 0) && !this->GetLabel(targetValue)->GetLocked()) // skip exterior and locked labels { targetIter.Set(sourceValue + numberOfTargetLabels); } ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::LayerContainerToImageProcessing(ImageType* target, unsigned int layer) { typename ImageType::Pointer itkSource; mitk::CastToItkImage(m_LayerContainer[layer], itkSource); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(target, target->GetLargestPossibleRegion()); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::ImageToLayerContainerProcessing(ImageType* source, unsigned int layer) const { typename ImageType::Pointer itkTarget; mitk::CastToItkImage(m_LayerContainer[layer], itkTarget); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter(source, source->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion()); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::EraseLabelProcessing(ImageType* itkImage, PixelType pixelValue, unsigned int /*layer*/) { typedef itk::ImageRegionIterator< ImageType > IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { PixelType value = iter.Get(); if (value == pixelValue) { iter.Set(0); } ++iter; } } template < typename ImageType > void mitk::LabelSetImage::MergeLabelProcessing(ImageType* itkImage, PixelType pixelValue, PixelType index) { typedef itk::ImageRegionIterator< ImageType > IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { if (iter.Get() == index) { iter.Set(pixelValue); } ++iter; } } bool mitk::Equal(const mitk::LabelSetImage& leftHandSide, const mitk::LabelSetImage& rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; /* LabelSetImage members */ MITK_INFO(verbose) << "--- LabelSetImage Equal ---"; // number layers returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers(); if (!returnValue) { MITK_INFO(verbose) << "Number of layers not equal."; return false; } // total number labels returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels(); if (!returnValue) { MITK_INFO(verbose) << "Total number of labels not equal."; return false; } // active layer returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer(); if (!returnValue) { MITK_INFO(verbose) << "Active layer not equal."; return false; } // working image data returnValue = mitk::Equal((const mitk::Image&)leftHandSide, (const mitk::Image&)rightHandSide, eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Working image data not equal."; return false; } for (unsigned int layerIndex = 0; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++) { // layer image data returnValue = mitk::Equal(*leftHandSide.GetLayerImage(layerIndex), *rightHandSide.GetLayerImage(layerIndex), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Layer image data not equal."; return false; } // layer labelset data returnValue = mitk::Equal(*leftHandSide.GetLabelSet(layerIndex), *rightHandSide.GetLabelSet(layerIndex), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Layer labelset data not equal."; return false; } } return returnValue; -} \ No newline at end of file +} diff --git a/Modules/Multilabel/mitkLabelSetImage.h b/Modules/Multilabel/mitkLabelSetImage.h index 5d1e74c0a7..afa6143ed7 100644 --- a/Modules/Multilabel/mitkLabelSetImage.h +++ b/Modules/Multilabel/mitkLabelSetImage.h @@ -1,341 +1,352 @@ /*=================================================================== 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 __mitkLabelSetImage_H_ #define __mitkLabelSetImage_H_ #include "mitkImage.h" #include "MitkMultilabelExports.h" #include #include #include #include -#include namespace mitk { //##Documentation //## @brief LabelSetImage class for handling labels and layers in a segmentation session. //## //## Handles operations for adding, removing, erasing and editing labels and layers. //## @ingroup Data class MITKMULTILABEL_EXPORT LabelSetImage : public Image { public: mitkClassMacro(LabelSetImage, Image) itkNewMacro(Self) typedef mitk::Label::PixelType PixelType; - typedef itk::Image< PixelType, 3 > LabelSetImageType; - typedef itk::VariableLengthVector< PixelType > VariableVectorType; - typedef itk::VectorImage< PixelType, 3 > VectorImageType; - typedef itk::VectorImageToImageAdaptor< PixelType, 3 > ImageAdaptorType; - /** * \brief BeforeChangeLayerEvent (e.g. used for GUI integration) * As soon as active labelset should be changed, the signal emits. * Emitted by SetActiveLayer(int layer); */ Message<> BeforeChangeLayerEvent; /** * \brief AfterchangeLayerEvent (e.g. used for GUI integration) * As soon as active labelset was changed, the signal emits. * Emitted by SetActiveLayer(int layer); */ Message<> AfterchangeLayerEvent; /** * @brief Initialize an empty mitk::LabelSetImage using the information * of an mitk::Image * @param image the image which is used for initializing the mitk::LabelSetImage */ using mitk::Image::Initialize; virtual void Initialize(const mitk::Image* image) override; /** * \brief */ void Concatenate(mitk::LabelSetImage* image); /** * \brief */ void ClearBuffer(); /** * @brief Merges the mitk::Label with a given target value with the active label * @param targetPixelValue the value of the mitk::Label that should be merged with the active one * @param layer the layer in which the merge should be performed */ void MergeLabel(PixelType targetPixelValue, unsigned int layer = 0); /** * @brief Merges a list of mitk::Labels with the mitk::Label that has a specific value * @param VectorOfLablePixelValues the list of labels that should be merge with the specified one * @param index the value of the label into which the other should be merged * @param layer the layer in which the merge should be performed */ void MergeLabels(std::vector& VectorOfLablePixelValues, PixelType index, unsigned int layer = 0); /** * \brief */ void UpdateCenterOfMass(PixelType pixelValue, unsigned int layer =0); /** * @brief Removes labels from the mitk::LabelSet of given layer. * Calls mitk::LabelSetImage::EraseLabels() which also removes the labels from within the image. * @param VectorOfLabelPixelValues a list of labels to be removed * @param layer the layer in which the labels should be removed */ void RemoveLabels(std::vector& VectorOfLabelPixelValues, unsigned int layer = 0); /** * @brief Erases the label with the given value in the given layer from the underlying image. * The label itself will not be erased from the respective mitk::LabelSet. In order to * remove the label itself use mitk::LabelSetImage::RemoveLabels() * @param pixelValue the label which will be remove from the image * @param layer the layer in which the label should be removed */ void EraseLabel(PixelType pixelValue, unsigned int layer = 0); /** * @brief Similar to mitk::LabelSetImage::EraseLabel() this funtion erase a list of labels from the image * @param VectorOfLabelPixelValues the list of labels that should be remove * @param layer the layer for which the labels should be removed */ void EraseLabels(std::vector& VectorOfLabelPixelValues, unsigned int layer = 0); /** * \brief Returns true if the value exists in one of the labelsets*/ bool ExistLabel(PixelType pixelValue) const; /** * @brief Checks if a label exists in a certain layer * @param pixelValue the label value * @param layer the layer in which should be searched for the label * @return true if the label exists otherwise false */ bool ExistLabel(PixelType pixelValue, unsigned int layer) const; /** * \brief Returns true if the labelset exists*/ bool ExistLabelSet(unsigned int layer) const; /** * @brief Returns the active label of a specific layer * @param layer the layer ID for which the active label should be returned * @return the active label of the specified layer */ mitk::Label* GetActiveLabel(unsigned int layer = 0); /** * @brief Returns the mitk::Label with the given pixelValue and for the given layer * @param pixelValue the pixel value of the label * @param layer the layer in which the labels should be located * @return the mitk::Label if available otherwise NULL */ mitk::Label* GetLabel(PixelType pixelValue, unsigned int layer = 0) const; /** * @brief Returns the currently active mitk::LabelSet * @return the mitk::LabelSet of the active layer or NULL if non is present */ mitk::LabelSet* GetActiveLabelSet(); /** * @brief Gets the mitk::LabelSet for the given layer * @param layer the layer for which the mitk::LabelSet should be retrieved * @return the respective mitk::LabelSet or NULL if non exists for the given layer */ mitk::LabelSet * GetLabelSet(unsigned int layer = 0); const mitk::LabelSet * GetLabelSet(unsigned int layer = 0) const; /** * @brief Gets the ID of the currently active layer * @return the ID of the active layer */ unsigned int GetActiveLayer() const; /** * @brief Get the number of all existing mitk::Labels for a given layer * @param layer the layer ID for which the active mitk::Labels should be retrieved * @return the number of all existing mitk::Labels for the given layer */ unsigned int GetNumberOfLabels(unsigned int layer = 0) const; /** * @brief Returns the number of all labels summed up across all layers * @return the overall number of labels across all layers */ unsigned int GetTotalNumberOfLabels() const; - /** - * \brief */ - void SurfaceStamp(mitk::Surface* surface, bool forceOverwrite); + // This function will need to be ported to an external class + // it requires knowledge of pixeltype and dimension and includes + // too much algorithm to be sensibly part of a data class + ///** + // * \brief */ + //void SurfaceStamp(mitk::Surface* surface, bool forceOverwrite); /** * \brief */ mitk::Image::Pointer CreateLabelMask(PixelType index); /** * @brief Initialize a new mitk::LabelSetImage by an given image. * For all distinct pixel values of the parameter image new labels will * be created. If the number of distinct pixel values exceeds mitk::Label::MAX_LABEL_VALUE * a new layer will be created * @param image the image which is used for initialization */ void InitializeByLabeledImage(mitk::Image::Pointer image); /** * \brief */ void MaskStamp(mitk::Image* mask, bool forceOverwrite); /** * \brief */ void SetActiveLayer(unsigned int layer); /** * \brief */ unsigned int GetNumberOfLayers() const; - /** - * \brief */ - VectorImageType::Pointer GetVectorImage(bool forceUpdate) const; - - /** - * \brief */ - void SetVectorImage(VectorImageType::Pointer image ); - /** * @brief Adds a new layer to the LabelSetImage. The new layer will be set as the active one * @param layer a mitk::LabelSet which will be set as new layer. * @return the layer ID of the new layer */ unsigned int AddLayer(mitk::LabelSet::Pointer layer =nullptr); + /** + * \brief Add a layer based on a provided mitk::Image + * \param layerImage is added to the vector of label images + * \param lset a label set that will be added to the new layer if provided + *\return the layer ID of the new layer + */ + unsigned int AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer lset = nullptr); + + /** + * \brief Add a LabelSet to an existing layer + * + * This will replace an existing labelSet if one exists. Throws an exceptions if you are trying + * to add a labelSet to a non-existing layer. + * + * If there are no labelSets for layers with an id less than layerIdx default ones will be added + * for them. + * + * \param layerIdx The index of the layer the LabelSet should be added to + * \param labelSet The LabelSet that should be added + */ + void AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet); + /** * @brief Removes the active layer and the respective mitk::LabelSet and image information. * The new active layer is the one below, if exists */ void RemoveLayer(); /** * \brief */ mitk::Image* GetLayerImage(unsigned int layer); const mitk::Image* GetLayerImage(unsigned int layer) const; void OnLabelSetModified(); /** * @brief Sets the label which is used as default exterior label when creating a new layer * @param label the label which will be used as new exterior label */ void SetExteriorLabel(mitk::Label * label); /** * @brief Gets the mitk::Label which is used as default exterior label * @return the exterior mitk::Label */ mitk::Label* GetExteriorLabel(); const mitk::Label* GetExteriorLabel() const; protected: mitkCloneMacro(Self) LabelSetImage(); LabelSetImage(const LabelSetImage & other); virtual ~LabelSetImage(); template < typename ImageType1, typename ImageType2 > void ChangeLayerProcessing( ImageType1* source, ImageType2* target ); template < typename ImageType > void LayerContainerToImageProcessing( ImageType* source, unsigned int layer); template < typename ImageType > void ImageToLayerContainerProcessing( ImageType* source, unsigned int layer) const; template < typename ImageType > void CalculateCenterOfMassProcessing( ImageType* input, PixelType index, unsigned int layer); template < typename ImageType > void ClearBufferProcessing( ImageType* input); template < typename ImageType > void EraseLabelProcessing( ImageType* input, PixelType index, unsigned int layer); // template < typename ImageType > // void ReorderLabelProcessing( ImageType* input, int index, int layer); template < typename ImageType > void MergeLabelProcessing( ImageType* input, PixelType pixelValue, PixelType index); template < typename ImageType > void ConcatenateProcessing( ImageType* input, mitk::LabelSetImage* other); template < typename ImageType > void MaskStampProcessing( ImageType* input, mitk::Image* mask, bool forceOverwrite); template < typename ImageType > void CreateLabelMaskProcessing( ImageType* input, mitk::Image* mask, PixelType index); template < typename ImageType1, typename ImageType2 > void InitializeByLabeledImageProcessing( ImageType1* input, ImageType2* other); std::vector< LabelSet::Pointer > m_LabelSetContainer; std::vector< Image::Pointer > m_LayerContainer; int m_ActiveLayer; mitk::Label::Pointer m_ExteriorLabel; }; /** * @brief Equal A function comparing two label set images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - LabelSetImage members * - working image data * - layer image data * - labels in label set * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKMULTILABEL_EXPORT bool Equal( const mitk::LabelSetImage& leftHandSide, const mitk::LabelSetImage& rightHandSide, ScalarType eps, bool verbose ); } // namespace mitk #endif // __mitkLabelSetImage_H_ diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.cpp b/Modules/Multilabel/mitkLabelSetImageConverter.cpp new file mode 100644 index 0000000000..51e5276874 --- /dev/null +++ b/Modules/Multilabel/mitkLabelSetImageConverter.cpp @@ -0,0 +1,156 @@ +/*=================================================================== + +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 "mitkLabelSetImageConverter.h" + +// mitk includes +#include "mitkImageAccessByItk.h" +#include "mitkImageCast.h" +#include + +// itk includes +#include "itkImage.h" +#include "itkVectorImage.h" +#include +#include +#include "itkImageDuplicator.h" + +template < typename TPixel, unsigned int VImageDimension > +void VectorOfMitkImagesToMitkVectorImage(const itk::Image* /*source*/, mitk::Image::Pointer &output, mitk::LabelSetImage::ConstPointer input) +{ + typedef itk::ComposeImageFilter< itk::Image > ComposeFilterType; + + unsigned int numberOfLayers = input->GetNumberOfLayers(); + // 2015/07/01 At the time of writing MITK has problems with mitk::Images encapsulating itk::VectorImage + // if the vector length is less than 2, which might very well happen for segmentations. + if ( numberOfLayers > 1 ) + { // if we have only one image we do not need to create a vector + typename ComposeFilterType::Pointer vectorImageComposer = ComposeFilterType::New(); + + unsigned int activeLayer = input->GetActiveLayer(); + for (unsigned int layer(0); layer < numberOfLayers; layer++) + { + typename itk::Image::Pointer itkCurrentLayer; + // for the active layer use the current state not the saved one in the vector + if (layer == activeLayer) + { + mitk::CastToItkImage(dynamic_cast(input.GetPointer()), itkCurrentLayer); + } + else + { + mitk::CastToItkImage(input->GetLayerImage(layer), itkCurrentLayer); + } + + vectorImageComposer->SetInput(layer, itkCurrentLayer); + } + + try + { + vectorImageComposer->Update(); + } + catch (const itk::ExceptionObject& e) + { + MITK_ERROR << "Caught exception while updating compose filter: " << e.what(); + } + + output = mitk::GrabItkImageMemory(vectorImageComposer->GetOutput()); + } + else + { + // we want the clone to be a mitk::Image, not a mitk::LabelSetImage + typename itk::Image::Pointer itkCurrentLayer; + mitk::CastToItkImage(dynamic_cast(input.GetPointer()), itkCurrentLayer); + typedef itk::ImageDuplicator< itk::Image > DuplicatorType; + typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); + duplicator->SetInputImage(itkCurrentLayer); + duplicator->Update(); + + output = mitk::GrabItkImageMemory(duplicator->GetOutput()); + } +} + +mitk::Image::Pointer mitk::LabelSetImageConverter::ConvertLabelSetImageToImage(const mitk::LabelSetImage::ConstPointer input) +{ + unsigned int numberOfLayers = input->GetNumberOfLayers(); + + if (numberOfLayers == 0) + { + mitkThrow() << "Tried to convert LabelSetImage without layers"; + } + + mitk::Image::Pointer output; + AccessByItk_2(input->GetLayerImage(0), VectorOfMitkImagesToMitkVectorImage, output, input); + + return output; +} + +template +void MitkImageToMitkLabelSetImage(itk::VectorImage< TPixel, VDimensions> * source, mitk::LabelSetImage::Pointer &output) +{ + typedef itk::VectorImage< TPixel, VDimensions > VectorImageType; + typedef itk::Image< TPixel, VDimensions > ImageType; + typedef itk::VectorIndexSelectionCastImageFilter< VectorImageType, ImageType > VectorIndexSelectorType; + + unsigned int numberOfComponents = source->GetVectorLength(); + + if (numberOfComponents < 1) + { + mitkThrow() << "At least one Component is required."; + } + + typename VectorIndexSelectorType::Pointer vectorIndexSelector = VectorIndexSelectorType::New(); + + vectorIndexSelector->SetIndex(0); + vectorIndexSelector->SetInput(source); + vectorIndexSelector->Update(); + + mitk::Image::Pointer tempImage; + mitk::CastToMitkImage(vectorIndexSelector->GetOutput(), tempImage); + + output = mitk::LabelSetImage::New(); + output->InitializeByLabeledImage(tempImage); + + for (unsigned int layer = 1; layer < numberOfComponents; ++layer) + { + typename VectorIndexSelectorType::Pointer vectorIndexSelectorLoop = VectorIndexSelectorType::New(); + vectorIndexSelectorLoop->SetIndex(layer); + vectorIndexSelector->SetInput(source); + vectorIndexSelector->Update(); + + mitk::Image::Pointer loopImage; + mitk::CastToMitkImage(vectorIndexSelector->GetOutput(), loopImage); + + output->AddLayer(loopImage); + } + +} + +mitk::LabelSetImage::Pointer mitk::LabelSetImageConverter::ConvertImageToLabelSetImage(const mitk::Image::Pointer input) +{ + mitk::LabelSetImage::Pointer output; + + if (input->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::ImageIOBase::VECTOR) + { + AccessVectorPixelTypeByItk_n(input, MitkImageToMitkLabelSetImage, (output)); + } + else + { + output = mitk::LabelSetImage::New(); + output->InitializeByLabeledImage( input ); + } + + return output; +} diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.h b/Modules/Multilabel/mitkLabelSetImageConverter.h new file mode 100644 index 0000000000..86f687ad85 --- /dev/null +++ b/Modules/Multilabel/mitkLabelSetImageConverter.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 __mitkLabelSetImageConverter_H_ +#define __mitkLabelSetImageConverter_H_ + +// for export +#include "MitkMultilabelExports.h" + +// mitk includes +#include "mitkImage.h" +#include "mitkLabelSetImage.h" + + +namespace mitk +{ + + /** + * \brief Converts mitkLabelSetImage into a mitk::Image containing an itk::VectorImage + * + * Convenience class to avoid the dimension templating hassle. + */ + +class MITKMULTILABEL_EXPORT LabelSetImageConverter +{ + +public: + /// \brief Converts a mitk::LabelSetImage to an a itk::VectorImage containing mitk::Image, templating is internal + static mitk::Image::Pointer ConvertLabelSetImageToImage( const mitk::LabelSetImage::ConstPointer input ); + + /// \brief Converst a mitk::Image to a mitk::LabelSetImage, templating and differentation between itk::Image and itk::VectorImage is internal + static mitk::LabelSetImage::Pointer ConvertImageToLabelSetImage( const mitk::Image::Pointer input ); + +protected: + +}; + +} // namespace mitk + +#endif // __mitkLabelSetImageConverter_H_ diff --git a/Modules/Multilabel/mitkLabelSetImageIO.cpp b/Modules/Multilabel/mitkLabelSetImageIO.cpp index ceb185a674..6206af7b18 100644 --- a/Modules/Multilabel/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/mitkLabelSetImageIO.cpp @@ -1,512 +1,694 @@ /*=================================================================== 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 __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkLabelSetImageIO.h" +#include "mitkLabelSetImageConverter.h" // itk #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" +#include "mitkImageAccessByItk.h" #include "tinyxml.h" + + namespace mitk { 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 LabelSetImage* input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); const LabelSetImage* input = static_cast(this->GetInput()); const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { mitkThrow() << "Could not set locale " << currLocale; } } - typedef itk::ImageFileWriter WriterType; + mitk::Image::Pointer inputVector = mitk::LabelSetImageConverter::ConvertLabelSetImageToImage(input); - VectorImageType::Pointer vectorImage = input->GetVectorImage(true); // force update - - char keybuffer[512]; - char valbuffer[512]; + // image write + if ( inputVector.IsNull() ) + { + mitkThrow() << "Cannot write non-image data"; + } - sprintf( keybuffer, "modality"); - sprintf( valbuffer, "org.mitk.image.multilabel"); - itk::EncapsulateMetaData(vectorImage->GetMetaDataDictionary(),std::string(keybuffer), std::string(valbuffer)); + itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); - sprintf( keybuffer, "layers"); - sprintf( valbuffer, "%1d", input->GetNumberOfLayers()); - itk::EncapsulateMetaData(vectorImage->GetMetaDataDictionary(),std::string(keybuffer), std::string(valbuffer)); + // Clone the image geometry, because we might have to change it + // for writing purposes + BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); - for (unsigned int layerIdx=0; layerIdxGetNumberOfLayers(); layerIdx++) + // Check if geometry information will be lost + if (inputVector->GetDimension() == 2 && + !geometry->Is2DConvertable()) { - sprintf( keybuffer, "layer_%03d", layerIdx ); // layer idx - sprintf( valbuffer, "%1d", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer - itk::EncapsulateMetaData(vectorImage->GetMetaDataDictionary(),std::string(keybuffer), std::string(valbuffer)); + 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; - mitk::LabelSet::LabelContainerConstIteratorType iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); - unsigned int count(0); - while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) + 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++) { - std::auto_ptr document; - document.reset(new TiXmlDocument()); - - TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); // TODO what to write here? encoding? etc.... - document->LinkEndChild( decl ); - TiXmlElement * labelElem = 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(vectorImage->GetMetaDataDictionary(),std::string(keybuffer), printer.Str()); - ++iter; - ++count; + 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)); } - } - itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); - io->SetFileType( itk::ImageIOBase::Binary ); - io->UseCompressionOn(); + //use compression if available + nrrdImageIo->UseCompressionOn(); - WriterType::Pointer nrrdWriter = WriterType::New(); - nrrdWriter->UseInputMetaDataDictionaryOn(); - nrrdWriter->SetInput(vectorImage); + nrrdImageIo->SetIORegion(ioRegion); + nrrdImageIo->SetFileName(path); - LocalFile localFile(this); - nrrdWriter->SetFileName(localFile.GetFileName().c_str()); - nrrdWriter->UseCompressionOn(); - nrrdWriter->SetImageIO(io); + // label set specific meta data + char keybuffer[512]; + char valbuffer[512]; - try - { - nrrdWriter->Update(); + 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; layerIdxGetNumberOfLayers(); layerIdx++) + { + sprintf(keybuffer, "layer_%03d", layerIdx); // layer idx + sprintf(valbuffer, "%1d", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer + itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); + + mitk::LabelSet::LabelContainerConstIteratorType iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); + unsigned int count(0); + while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) + { + std::auto_ptr document; + document.reset(new TiXmlDocument()); + + TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... + document->LinkEndChild(decl); + TiXmlElement * labelElem = 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 + + ImageReadAccessor imageAccess(inputVector); + nrrdImageIo->Write(imageAccess.GetData()); } - catch (itk::ExceptionObject e) + catch (const std::exception& e) { - MITK_ERROR << "Could not write segmentation session. See error log for details."; - mitkThrow() << e.GetDescription(); + mitkThrow() << e.what(); } + // end image write try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { mitkThrow() << "Could not reset locale " << currLocale; } } 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::Read() { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { mitkThrow() << "Could not set locale."; } } - LabelSetImage::VectorImageType::Pointer vectorImage; + // begin regular image loading, adapted from mitkItkImageIO + itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); + Image::Pointer image = Image::New(); - typedef itk::ImageFileReader FileReaderType; - FileReaderType::Pointer reader = FileReaderType::New(); - reader->SetFileName(this->GetLocalFileName()); - itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); - reader->SetImageIO(io); - try - { - reader->Update(); - } - catch(itk::ExceptionObject& e) + 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() << e.GetDescription(); + mitkThrow() << "Empty filename in mitk::ItkImageIO "; } - catch(...) + + // 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) { - mitkThrow() << "Unknown exception caught!"; + MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; + ndim = MAXDIM; } - vectorImage = reader->GetOutput(); + 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; - if (vectorImage.IsNull()) - mitkThrow() << "Could not retrieve the vector image."; + ScalarType spacing[MAXDIM]; + spacing[0] = 1.0f; + spacing[1] = 1.0f; + spacing[2] = 1.0f; + spacing[3] = 1.0f; - LabelSetImage::Pointer output = LabelSetImage::New(); + Point3D origin; + origin.Fill(0); - ImageType::Pointer auxImg = ImageType::New(); - auxImg->SetSpacing( vectorImage->GetSpacing() ); - auxImg->SetOrigin( vectorImage->GetOrigin() ); - auxImg->SetDirection( vectorImage->GetDirection() ); - auxImg->SetRegions( vectorImage->GetLargestPossibleRegion() ); - auxImg->Allocate(); + unsigned int i; + for (i = 0; i < ndim; ++i) + { + ioStart[i] = 0; + ioSize[i] = nrrdImageIO->GetDimensions(i); + if (iGetDimensions(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 + ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); + timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); + image->SetTimeGeometry(timeGeometry); + + buffer = NULL; + MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; + + const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); + for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); + iter != iterEnd; ++iter) + { + std::string key = std::string("meta.") + iter->first; + if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) + { + std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); + image->SetProperty(key.c_str(), mitk::StringProperty::New(value)); + } + } - // initialize output image based on vector image meta information - output->InitializeByItk( auxImg.GetPointer() ); + // end regular image loading - itk::MetaDataDictionary imgMetaDictionary = vectorImage->GetMetaDataDictionary(); + LabelSetImage::Pointer output = LabelSetImageConverter::ConvertImageToLabelSetImage(image); + // get labels and add them as properties to the image char keybuffer[256]; - int numberOfLayers = GetIntByKey(imgMetaDictionary,"layers"); + unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; - for( int layerIdx=0; layerIdx < numberOfLayers; layerIdx++) + for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { - sprintf( keybuffer, "layer_%03d", layerIdx ); - int numberOfLabels = GetIntByKey(imgMetaDictionary,keybuffer); + sprintf(keybuffer, "layer_%03d", layerIdx); + int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); - for(int labelIdx=0; labelIdx < numberOfLabels; labelIdx++) + for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { TiXmlDocument doc; - sprintf( keybuffer, "label_%03d_%05d", layerIdx, labelIdx ); - _xmlStr = GetStringByKey(imgMetaDictionary,keybuffer); + sprintf(keybuffer, "label_%03d_%05d", layerIdx, labelIdx); + _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str()); TiXmlElement * labelElem = doc.FirstChildElement("Label"); if (labelElem == 0) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = LoadLabelFromTiXmlDocument(labelElem); - if(label->GetValue() == 0) // set exterior label is needed to hold exterior information + if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); + labelSet->SetLayer(layerIdx); } - output->AddLayer(labelSet); + output->AddLabelSetToLayer(layerIdx, labelSet); } - // set vector image - output->SetVectorImage(vectorImage); + MITK_INFO << "...finished!" << std::endl; try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { mitkThrow() << "Could not reset locale!"; } 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; } Label::Pointer LabelSetImageIO::LoadLabelFromTiXmlDocument(TiXmlElement * labelElem) { // reread TiXmlElement * propElem = labelElem->FirstChildElement("property"); std::string name; mitk::BaseProperty::Pointer prop; mitk::Label::Pointer label = mitk::Label::New(); while(propElem) { LabelSetImageIO::PropertyFromXmlElem( name, prop, propElem ); label->SetProperty( name, prop ); propElem = propElem->NextSiblingElement( "property" ); } return label.GetPointer(); } bool LabelSetImageIO::PropertyFromXmlElem(std::string& key, mitk::BaseProperty::Pointer& prop, TiXmlElement* elem) { std::string type; elem->QueryStringAttribute("type", &type); elem->QueryStringAttribute("key", &key); // construct name of serializer class std::string serializername(type); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << type << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one."; for ( std::list::iterator iter = allSerializers.begin(); iter != allSerializers.end(); ++iter ) { if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) { try { prop = serializer->Deserialize(elem->FirstChildElement()); } catch (std::exception& e) { MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what(); return false; } break; } } if(prop.IsNull()) return false; return true; } void LabelSetImageIO::LoadLabelSetImagePreset(const std::string & presetFilename, mitk::LabelSetImage::Pointer& inputImage ) { std::unique_ptr presetXmlDoc(new TiXmlDocument()); bool ok = presetXmlDoc->LoadFile(presetFilename); if ( !ok ) return; TiXmlElement * presetElem = presetXmlDoc->FirstChildElement("LabelSetImagePreset"); if(!presetElem) { MITK_INFO << "No valid preset XML"; return; } int numberOfLayers; presetElem->QueryIntAttribute("layers", &numberOfLayers); for(int i = 0 ; i < numberOfLayers; i++) { TiXmlElement * layerElem = presetElem->FirstChildElement("Layer"); int numberOfLabels; layerElem->QueryIntAttribute("labels", &numberOfLabels); if(inputImage->GetLabelSet(i) == NULL) inputImage->AddLayer(); TiXmlElement * labelElement = layerElem->FirstChildElement("Label"); if(labelElement == NULL) break; for(int j = 0 ; j < numberOfLabels; j++) { mitk::Label::Pointer label = mitk::LabelSetImageIO::LoadLabelFromTiXmlDocument(labelElement); inputImage->GetLabelSet()->AddLabel(label); labelElement = labelElement->NextSiblingElement("Label"); if(labelElement == NULL) break; } } } LabelSetImageIO* LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } TiXmlElement * LabelSetImageIO::GetLabelAsTiXmlElement(Label * label) { TiXmlElement* labelElem = new TiXmlElement("Label"); // add XML contents const PropertyList::PropertyMap* propmap = label->GetMap(); for ( PropertyList::PropertyMap::const_iterator iter = propmap->begin(); iter != propmap->end(); ++iter ) { std::string key = iter->first; const BaseProperty* property = iter->second; TiXmlElement* element = PropertyToXmlElem( key, property ); if (element) labelElem->LinkEndChild( element ); } return labelElem; } TiXmlElement* LabelSetImageIO::PropertyToXmlElem( const std::string& key, const BaseProperty* property ) { TiXmlElement* keyelement = new TiXmlElement("property"); keyelement->SetAttribute("key", key); keyelement->SetAttribute("type", property->GetNameOfClass()); // construct name of serializer class std::string serializername(property->GetNameOfClass()); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one."; for ( std::list::iterator iter = allSerializers.begin(); iter != allSerializers.end(); ++iter ) { if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) { serializer->SetProperty(property); try { TiXmlElement* valueelement = serializer->Serialize(); if (valueelement) keyelement->LinkEndChild( valueelement ); } catch (std::exception& e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } return keyelement; } bool LabelSetImageIO::SaveLabelSetImagePreset(const std::string & presetFilename, LabelSetImage::Pointer & inputImage) { TiXmlDocument * presetXmlDoc = new TiXmlDocument(); TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); presetXmlDoc->LinkEndChild( decl ); TiXmlElement * presetElement = new TiXmlElement("LabelSetImagePreset"); presetElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); presetXmlDoc->LinkEndChild(presetElement); for (unsigned int layerIdx=0; layerIdxGetNumberOfLayers(); layerIdx++) { TiXmlElement * layerElement = new TiXmlElement("Layer"); layerElement->SetAttribute("index", layerIdx); layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIdx)); presetElement->LinkEndChild(layerElement); for (unsigned int labelIdx=0; labelIdxGetNumberOfLabels(layerIdx); labelIdx++) { TiXmlElement * labelAsXml = LabelSetImageIO::GetLabelAsTiXmlElement(inputImage->GetLabel(labelIdx,layerIdx)); layerElement->LinkEndChild(labelAsXml); } } bool wasSaved = presetXmlDoc->SaveFile(presetFilename); delete presetXmlDoc; return wasSaved; } } //namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/mitkLabelSetImageIO.h b/Modules/Multilabel/mitkLabelSetImageIO.h index 749486cf41..cac93c1939 100644 --- a/Modules/Multilabel/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/mitkLabelSetImageIO.h @@ -1,81 +1,77 @@ /*=================================================================== 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 __mitkLabelSetImageWriter_h #define __mitkLabelSetImageWriter_h #include "MitkMultilabelExports.h" #include #include namespace mitk { /** * Writes a LabelSetImage to a file * @ingroup Process */ // The export macro should be removed. Currently, the unit // tests directly instantiate this class. class MITKMULTILABEL_EXPORT LabelSetImageIO : public mitk::AbstractFileIO { public: - typedef LabelSetImage::PixelType PixelType; - typedef LabelSetImage::LabelSetImageType ImageType; - typedef LabelSetImage::VectorImageType VectorImageType; - typedef mitk::LabelSetImage InputType; LabelSetImageIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; /** * @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 */ virtual std::vector Read() override; virtual ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- virtual void Write() override; virtual ConfidenceLevel GetWriterConfidenceLevel() const override; // -------------- LabelSetImageIO specific functions ------------- static bool SaveLabelSetImagePreset(const std::string & presetFilename, mitk::LabelSetImage::Pointer & inputImage); int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); static Label::Pointer LoadLabelFromTiXmlDocument(TiXmlElement *labelElem); static void LoadLabelSetImagePreset(const std::string &presetFilename, mitk::LabelSetImage::Pointer &inputImage); private: LabelSetImageIO* IOClone() const override; static TiXmlElement *GetLabelAsTiXmlElement(Label *label); static TiXmlElement * PropertyToXmlElem( const std::string& key, const BaseProperty* property ); static bool PropertyFromXmlElem(std::string& key, mitk::BaseProperty::Pointer& prop, TiXmlElement* elem); }; } // end of namespace mitk #endif // __mitkLabelSetImageWriter_h diff --git a/Modules/QtWidgets/resource/LabelSetImage_48.png b/Modules/QtWidgets/resource/LabelSetImage_48.png new file mode 100644 index 0000000000..b1117f4696 Binary files /dev/null and b/Modules/QtWidgets/resource/LabelSetImage_48.png differ diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index ba779c5657..2b4501588d 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -1,14 +1,15 @@ - + Binaerbilder_48.png Images_48.png PointSet_48.png Segmentation_48.png Surface_48.png mm_pointer.png mm_scroll.png mm_zoom.png mm_contrast.png mm_pan.png + LabelSetImage_48.png diff --git a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp index 2969362b5b..da0344c151 100644 --- a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp +++ b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp @@ -1,180 +1,184 @@ /*=================================================================== 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 "QmitkNodeDescriptorManager.h" #include #include #include #include #include #include #include #include QmitkNodeDescriptorManager* QmitkNodeDescriptorManager::GetInstance() { static QmitkNodeDescriptorManager _Instance; return &_Instance; /* static std::auto_ptr instance; if(instance.get() == 0) { instance.reset(new QmitkNodeDescriptorManager()); instance->Initialize(); } return instance.get();*/ } void QmitkNodeDescriptorManager::Initialize() { // Adding "Images" mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); this->AddDescriptor(new QmitkNodeDescriptor(tr("Image"), QString(":/Qmitk/Images_48.png"), isImage, this)); // Adding "Image Masks" mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isBinaryImage = mitk::NodePredicateAnd::New(isBinary, isImage); this->AddDescriptor(new QmitkNodeDescriptor(tr("ImageMask"), QString(":/Qmitk/Binaerbilder_48.png"), isBinaryImage, this)); + // Adding "LabelSet Image" + mitk::NodePredicateDataType::Pointer isLabelSetImage = mitk::NodePredicateDataType::New("LabelSetImage"); + this->AddDescriptor(new QmitkNodeDescriptor(tr("LabelSetImage"), QString(":/Qmitk/LabelSetImage_48.png"), isLabelSetImage, this)); + // Adding "PointSet" mitk::NodePredicateDataType::Pointer isPointSet = mitk::NodePredicateDataType::New("PointSet"); this->AddDescriptor(new QmitkNodeDescriptor(tr("PointSet"), QString(":/Qmitk/PointSet_48.png"), isPointSet, this)); // Adding "Surface" mitk::NodePredicateDataType::Pointer isSurface = mitk::NodePredicateDataType::New("Surface"); this->AddDescriptor(new QmitkNodeDescriptor(tr("Surface"), QString(":/Qmitk/Surface_48.png"), isSurface, this)); // Adding "NoneBinaryImages" mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New(isBinary); mitk::NodePredicateAnd::Pointer isNoneBinaryImage = mitk::NodePredicateAnd::New(isImage, isNotBinary); this->AddDescriptor(new QmitkNodeDescriptor(tr("NoneBinaryImage"), QString(":/Qmitk/Images_48.png"), isNoneBinaryImage, this)); } void QmitkNodeDescriptorManager::AddDescriptor( QmitkNodeDescriptor* _Descriptor ) { _Descriptor->setParent(this); m_NodeDescriptors.push_back(_Descriptor); } void QmitkNodeDescriptorManager::RemoveDescriptor( QmitkNodeDescriptor* _Descriptor ) { int index = m_NodeDescriptors.indexOf(_Descriptor); if(index != -1) { m_NodeDescriptors.removeAt(index); _Descriptor->setParent(0); delete _Descriptor; } } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetDescriptor( const mitk::DataNode* _Node ) const { QmitkNodeDescriptor* _Descriptor = m_UnknownDataNodeDescriptor; for(QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if((*it)->CheckNode(_Node)) _Descriptor = *it; } return _Descriptor; } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetDescriptor( const QString& _ClassName ) const { QmitkNodeDescriptor* _Descriptor = 0; if( _ClassName == "Unknown" ) { return m_UnknownDataNodeDescriptor; } else { for(QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if((*it)->GetClassName() == _ClassName) _Descriptor = *it; } } return _Descriptor; } QList QmitkNodeDescriptorManager::GetActions( const mitk::DataNode* _Node ) const { QList actions = m_UnknownDataNodeDescriptor->GetBatchActions(); actions.append(m_UnknownDataNodeDescriptor->GetActions()); QmitkNodeDescriptor* lastDescriptor = m_UnknownDataNodeDescriptor; for(QList::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if((*it)->CheckNode(_Node)) { actions.append(lastDescriptor->GetSeparator()); lastDescriptor = *it; actions.append(lastDescriptor->GetBatchActions()); actions.append(lastDescriptor->GetActions()); } } return actions; } QList QmitkNodeDescriptorManager::GetActions( const QList &_Nodes ) const { QList actions = m_UnknownDataNodeDescriptor->GetBatchActions(); QSet nodeDescriptors; QmitkNodeDescriptor* lastDescriptor; // find all descriptors for the nodes (unique) foreach (mitk::DataNode::Pointer node, _Nodes) { lastDescriptor = this->GetDescriptor(node); nodeDescriptors.insert(lastDescriptor); } // add all actions for the found descriptors lastDescriptor = m_UnknownDataNodeDescriptor; foreach (QmitkNodeDescriptor* descr, nodeDescriptors) { actions.append(lastDescriptor->GetSeparator()); lastDescriptor = descr; actions.append(lastDescriptor->GetBatchActions()); } return actions; } QmitkNodeDescriptorManager::QmitkNodeDescriptorManager() : m_UnknownDataNodeDescriptor(new QmitkNodeDescriptor("Unknown", QString(":/Qmitk/DataTypeUnknown_48.png"), 0, this)) { this->Initialize(); } QmitkNodeDescriptorManager::~QmitkNodeDescriptorManager() { //delete m_UnknownDataNodeDescriptor; //qDeleteAll(m_NodeDescriptors); } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetUnknownDataNodeDescriptor() const { return m_UnknownDataNodeDescriptor; } diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp index 7211860549..0d36ac0bbe 100644 --- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp @@ -1,446 +1,470 @@ /*=================================================================== 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 "mitkCorrectorAlgorithm.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" +#include "mitkITKImageImport.h" #include "mitkImageDataItem.h" #include "mitkContourUtils.h" #include "mitkLegacyAdaptors.h" #include #include "itkImageDuplicator.h" #include "itkImageRegionIterator.h" - -#include +#include "itkCastImageFilter.h" mitk::CorrectorAlgorithm::CorrectorAlgorithm() :ImageToImageFilter() +, m_FillColor( 1 ) +, m_EraseColor( 0 ) { } mitk::CorrectorAlgorithm::~CorrectorAlgorithm() { } +template +void ConvertBackToCorrectPixelType(itk::Image< TPixel, VDimensions> * reference, mitk::Image::Pointer target, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer segmentationPixelTypeImage) +{ + typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > InputImageType; + typedef itk::Image< TPixel, 2 > OutputImageType; + typedef itk::CastImageFilter< InputImageType, OutputImageType > CastImageFilterType; + + typename CastImageFilterType::Pointer castImageFilter = CastImageFilterType::New(); + castImageFilter->SetInput( segmentationPixelTypeImage ); + castImageFilter->Update(); + typename OutputImageType::Pointer tempItkImage = castImageFilter->GetOutput(); + tempItkImage->DisconnectPipeline(); + mitk::CastToMitkImage(tempItkImage, target); +} void mitk::CorrectorAlgorithm::GenerateData() { Image::Pointer inputImage = const_cast(ImageToImageFilter::GetInput(0)); if (inputImage.IsNull() || inputImage->GetDimension() != 2) { itkExceptionMacro("CorrectorAlgorithm needs a 2D image as input."); } if (m_Contour.IsNull()) { itkExceptionMacro("CorrectorAlgorithm needs a Contour object as input."); } // copy the input (since m_WorkingImage will be changed later) m_WorkingImage = inputImage; TimeGeometry::Pointer originalGeometry = NULL; if (inputImage->GetTimeGeometry() ) { originalGeometry = inputImage->GetTimeGeometry()->Clone(); m_WorkingImage->SetTimeGeometry( originalGeometry ); } else { itkExceptionMacro("Original image does not have a 'Time sliced geometry'! Cannot copy."); } Image::Pointer temporarySlice; - // Convert to ipMITKSegmentationTYPE (because TobiasHeimannCorrectionAlgorithm relys on that data type) + // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relys on that data type) { - itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; + itk::Image< DefaultSegmentationDataType, 2 >::Pointer correctPixelTypeImage; CastToItkImage( m_WorkingImage, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix - itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; + itk::Image< DefaultSegmentationDataType, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); //correctPixelTypeImage->SetDirection(imageDirection); temporarySlice = this->GetOutput(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); - m_FillColor = 1; - m_EraseColor = 0; ImprovedHeimannCorrectionAlgorithm(correctPixelTypeImage); - CastToMitkImage( correctPixelTypeImage, temporarySlice ); + + //this is suboptimal, needs to be kept synchronous to DefaultSegmentationDataType + if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT) + { //the cast at the beginning did not copy the data + CastToMitkImage(correctPixelTypeImage, temporarySlice); + } + else + { //it did copy the data and cast the pixel type + AccessByItk_n(m_WorkingImage, ConvertBackToCorrectPixelType, (temporarySlice, correctPixelTypeImage)); + } + } temporarySlice->SetTimeGeometry(originalGeometry); } -bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm(itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic) +bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm(itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic) { /*! Some documentation (not by the original author) TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line. There should be different results, depending on the line's properties: 1. Without any prior segmentation, the start point and the end point of the drawn line will be connected to a contour and the area enclosed by the contour will be marked as segmentation. 2. When the whole line is inside a segmentation, start and end point will be connected to a contour and the area of this contour will be subtracted from the segmentation. 3. When the line starts inside a segmentation and ends outside with only a single transition from segmentation to no-segmentation, nothing will happen. 4. When there are multiple transitions between inside-segmentation and outside-segmentation, the line will be divided in so called segments. Each segment is either fully inside or fully outside a segmentation. When it is inside a segmentation, its enclosed area will be subtracted from the segmentation. When the segment is outside a segmentation, its enclosed area it will be added to the segmentation. The algorithm is described in full length in Tobias Heimann's diploma thesis (MBI Technical Report 145, p. 37 - 40). */ ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice( m_WorkingImage, m_Contour, true, false ); bool firstPointIsFillingColor = false; if (projectedContour.IsNull() || projectedContour->GetNumberOfVertices() < 2 ) { return false; } // Read the first point of the contour ContourModel::VertexIterator contourIter = projectedContour->Begin(); if (contourIter == projectedContour->End()) return false; itk::Index<2> previousIndex; previousIndex[0] = (*contourIter)->Coordinates[0]; previousIndex[1] = (*contourIter)->Coordinates[1]; ++contourIter; int currentColor = ( pic->GetPixel(previousIndex) == m_FillColor); firstPointIsFillingColor = currentColor; TSegData currentSegment; int countOfSegments = 1; bool firstSegment = true; ContourModel::VertexIterator contourEnd = projectedContour->End(); for (; contourIter != contourEnd; ++contourIter) { // Get current point itk::Index<2> currentIndex; currentIndex[0] = (*contourIter)->Coordinates[0] +0.5; currentIndex[1] = (*contourIter)->Coordinates[1] +0.5; // Calculate length and slope double slopeX = currentIndex[0] - previousIndex[0]; double slopeY = currentIndex[1] - previousIndex[1]; double length = std::sqrt(slopeX * slopeX + slopeY * slopeY); double deltaX = slopeX / length; double deltaY = slopeY / length; for (double i = 0; i <= length && length > 0; i+=1) { itk::Index<2> temporaryIndex; temporaryIndex[0] = previousIndex[0] + deltaX * i; temporaryIndex[1] = previousIndex[1] + deltaY * i; if ( ! pic->GetLargestPossibleRegion().IsInside(temporaryIndex)) continue; if ( (pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor) { currentSegment.points.push_back(temporaryIndex); if ( ! firstSegment) { ModifySegment( currentSegment, pic); } else { firstSegment = false; } currentSegment = TSegData(); ++countOfSegments; currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor); } currentSegment.points.push_back(temporaryIndex); } previousIndex = currentIndex; } // Check if only on Segment if (firstSegment && currentSegment.points.size() > 0) { ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice( m_WorkingImage, m_Contour, true, false ); projectedContour->Close(); if (firstPointIsFillingColor) { ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_EraseColor); } else { ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_FillColor); } } return true; } -static void ColorSegment(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic, int fillColor, int eraseColor) +static void ColorSegment(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic, int fillColor, int eraseColor) { int colorMode = (pic->GetPixel(segment.points[0]) == fillColor); int color = 0; if (colorMode) color = eraseColor; else color = fillColor; std::vector< itk::Index<2> >::const_iterator indexIterator; std::vector< itk::Index<2> >::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); for (; indexIterator != indexEnd; ++indexIterator) { pic->SetPixel(*indexIterator, color); } } -static itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer CloneImage(itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic) +static itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer CloneImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) { - typedef itk::Image< ipMITKSegmentationTYPE, 2 > ItkImageType; + typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > ItkImageType; typedef itk::ImageDuplicator< ItkImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(pic); duplicator->Update(); return duplicator->GetOutput(); } -static itk::Index<2> GetFirstPoint(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic, int fillColor) +static itk::Index<2> GetFirstPoint(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic, int fillColor) { int colorMode = (pic->GetPixel(segment.points[0]) == fillColor); std::vector< itk::Index<2> >::const_iterator indexIterator; std::vector< itk::Index<2> >::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); for (; indexIterator != indexEnd; ++indexIterator) { itk::Index<2> index; for (int xOffset = -1 ; xOffset < 2; ++xOffset) { for (int yOffset = -1 ; yOffset < 2; ++yOffset) { index[0] = (*indexIterator)[0] - xOffset; index[1] = (*indexIterator)[1] - yOffset; if ((pic->GetPixel(index) == fillColor) != colorMode) { return index; } } } } mitkThrow() << "No Starting point is found next to the curve."; } -static std::vector > FindSeedPoints(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic, int fillColor) +static std::vector > FindSeedPoints(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic, int fillColor) { - typedef itk::Image< ipMITKSegmentationTYPE, 2 > ItkImageType; - typedef itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer ItkImagePointerType; + typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > ItkImageType; + typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer ItkImagePointerType; std::vector > seedPoints; try { itk::Index<2> firstPoint = GetFirstPoint(segment, pic, fillColor); seedPoints.push_back(firstPoint); } catch (mitk::Exception e) { return seedPoints; } if (segment.points.size() < 4) return seedPoints; std::vector< itk::Index<2> >::const_iterator indexIterator; std::vector< itk::Index<2> >::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); ItkImagePointerType listOfPoints = CloneImage(pic); listOfPoints->FillBuffer(0); listOfPoints->SetPixel(seedPoints[0],1); for (; indexIterator != indexEnd; ++indexIterator) { listOfPoints->SetPixel(*indexIterator, 2); } indexIterator = segment.points.begin(); indexIterator++; indexIterator++; indexEnd--; indexEnd--; for (; indexIterator != indexEnd; ++indexIterator) { bool pointFound = true; while (pointFound) { pointFound = false; itk::Index<2> index; itk::Index<2> index2; for (int xOffset = -1 ; xOffset < 2; ++xOffset) { for (int yOffset = -1 ; yOffset < 2; ++yOffset) { index[0] = (*indexIterator)[0] - xOffset; index[1] = (*indexIterator)[1] - yOffset; index2 = index; if (listOfPoints->GetPixel(index2) > 0) continue; index[0]--; if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2,1); continue; } index[0]=index[0] + 2; if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2,1); continue; } index[0]--; index[1]--; if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2,1); continue; } index[1]=index[1] + 2; if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2,1); continue; } } } } } return seedPoints; } -static int FillRegion(const std::vector > &seedPoints, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic, int fillColor, int eraseColor) +static int FillRegion(const std::vector > &seedPoints, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic, int fillColor, int eraseColor) { int numberOfPixel = 0; int mode = (pic->GetPixel(seedPoints[0]) == fillColor); int drawColor = fillColor; if (mode) { drawColor = eraseColor; } std::vector > workPoints; workPoints = seedPoints; // workPoints.push_back(seedPoints[0]); while (workPoints.size() > 0) { itk::Index<2> currentIndex = workPoints.back(); workPoints.pop_back(); if ((pic->GetPixel(currentIndex) == fillColor) == mode) ++numberOfPixel; pic->SetPixel(currentIndex, drawColor); currentIndex[0] -= 1; if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == fillColor) == mode) workPoints.push_back(currentIndex); currentIndex[0] += 2; if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == fillColor) == mode) workPoints.push_back(currentIndex); currentIndex[0] -= 1; currentIndex[1] -= 1; if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == fillColor) == mode) workPoints.push_back(currentIndex); currentIndex[1] += 2; if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == fillColor) == mode) workPoints.push_back(currentIndex); } return numberOfPixel; } -static void OverwriteImage(itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer source, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer target) +static void OverwriteImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer source, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer target) { - typedef itk::Image< ipMITKSegmentationTYPE, 2 > ItkImageType; + typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > ItkImageType; typedef itk::ImageRegionIterator ImageIteratorType; ImageIteratorType sourceIter(source, source->GetLargestPossibleRegion()); ImageIteratorType targetIter(target, target->GetLargestPossibleRegion()); while ( ! sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } -bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic) +bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment, itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic) { - typedef itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer ItkImagePointerType; + typedef itk::Image< DefaultSegmentationDataType, 2 >::Pointer ItkImagePointerType; ItkImagePointerType firstSideImage = CloneImage(pic); ColorSegment(segment, firstSideImage, m_FillColor, m_EraseColor); ItkImagePointerType secondSideImage = CloneImage(firstSideImage); std::vector > seedPoints = FindSeedPoints(segment,firstSideImage,m_FillColor); if (seedPoints.size() < 1) return false; int firstSidePixel = FillRegion(seedPoints, firstSideImage, m_FillColor, m_EraseColor); std::vector > secondSeedPoints = FindSeedPoints(segment,firstSideImage,m_FillColor); if ( secondSeedPoints.size() < 1) return false; int secondSidePixel = FillRegion(secondSeedPoints, secondSideImage, m_FillColor, m_EraseColor); if (firstSidePixel < secondSidePixel) { OverwriteImage(firstSideImage, pic); } else { OverwriteImage(secondSideImage, pic); } return true; } diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h index b2f0ca1868..8a345f7099 100644 --- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h @@ -1,91 +1,99 @@ /*=================================================================== 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 mitkCorrectorAlgorithmhIncluded #define mitkCorrectorAlgorithmhIncluded #include "mitkImageToImageFilter.h" #include #include "mitkContourModel.h" #include "ipSegmentation.h" +#include #include namespace mitk { /** * This class encapsulates an algorithm, which takes a 2D binary image and a contour. * The algorithm tests if the line begins and ends inside or outside a segmentation * and whether areas should be added to or subtracted from the segmentation shape. * * This class has two outputs: * \li the modified input image from GetOutput() * * The output image is a combination of the original input with the generated difference image. * * \sa CorrectorTool2D */ class MITKSEGMENTATION_EXPORT CorrectorAlgorithm : public ImageToImageFilter { public: mitkClassMacro(CorrectorAlgorithm, ImageToImageFilter); itkFactorylessNewMacro(Self) itkCloneMacro(Self) + typedef mitk::Label::PixelType DefaultSegmentationDataType; + /** * \brief User drawn contour */ void SetContour( ContourModel* contour){this->m_Contour = contour;} /** * \brief Calculated difference image. */ //itkGetObjectMacro(DifferenceImage, Image); + itkSetMacro(FillColor, int); + itkSetMacro(EraseColor, int); + + itkGetMacro(FillColor, int); + itkGetMacro(EraseColor, int); // used by TobiasHeimannCorrectionAlgorithm typedef struct { int lineStart; int lineEnd; bool modified; std::vector< itk::Index<2> > points; } TSegData; protected: CorrectorAlgorithm(); virtual ~CorrectorAlgorithm(); // does the actual processing virtual void GenerateData() override; - bool ImprovedHeimannCorrectionAlgorithm(itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic); - bool ModifySegment(const TSegData &segment, itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic); + bool ImprovedHeimannCorrectionAlgorithm(itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic); + bool ModifySegment(const TSegData &segment, itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic); Image::Pointer m_WorkingImage; ContourModel::Pointer m_Contour; Image::Pointer m_DifferenceImage; int m_FillColor; int m_EraseColor; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp index fd242ad7b6..0d3a396877 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp @@ -1,322 +1,322 @@ /*=================================================================== 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 "mitkBinaryThresholdULTool.h" #include "mitkToolManager.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageTimeSelector.h" #include #include #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" // us #include "usModule.h" #include "usModuleResource.h" #include "usGetModuleContext.h" #include "usModuleContext.h" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, BinaryThresholdULTool, "ThresholdingUL tool"); } mitk::BinaryThresholdULTool::BinaryThresholdULTool() :m_SensibleMinimumThresholdValue(-100), m_SensibleMaximumThresholdValue(+100), m_CurrentLowerThresholdValue(1), m_CurrentUpperThresholdValue(1) { m_ThresholdFeedbackNode = DataNode::New(); m_ThresholdFeedbackNode->SetProperty( "color", ColorProperty::New(0.0, 1.0, 0.0) ); m_ThresholdFeedbackNode->SetProperty( "name", StringProperty::New("Thresholding feedback") ); m_ThresholdFeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true)); m_ThresholdFeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); } mitk::BinaryThresholdULTool::~BinaryThresholdULTool() { } const char** mitk::BinaryThresholdULTool::GetXPM() const { return NULL; } us::ModuleResource mitk::BinaryThresholdULTool::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("TwoThresholds_48x48.png"); return resource; } const char* mitk::BinaryThresholdULTool::GetName() const { return "UL Threshold"; } void mitk::BinaryThresholdULTool::Activated() { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeForThresholding = m_OriginalImageNode; if ( m_NodeForThresholding.IsNotNull() ) { SetupPreviewNode(); } else { m_ToolManager->ActivateTool(-1); } } void mitk::BinaryThresholdULTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); m_NodeForThresholding = NULL; m_OriginalImageNode = NULL; try { if (DataStorage* storage = m_ToolManager->GetDataStorage()) { storage->Remove( m_ThresholdFeedbackNode ); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch(...) { // don't care } m_ThresholdFeedbackNode->SetData(NULL); } void mitk::BinaryThresholdULTool::SetThresholdValues(double lower, double upper) { if (m_ThresholdFeedbackNode.IsNotNull()) { m_CurrentLowerThresholdValue = lower; m_CurrentUpperThresholdValue = upper; UpdatePreview(); } } void mitk::BinaryThresholdULTool::AcceptCurrentThresholdValue() { CreateNewSegmentationFromThreshold(m_NodeForThresholding); RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdULTool::CancelThresholding() { m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdULTool::SetupPreviewNode() { if (m_NodeForThresholding.IsNotNull()) { Image::Pointer image = dynamic_cast( m_NodeForThresholding->GetData() ); Image::Pointer originalImage = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { mitk::Image* workingimage = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if(workingimage) m_ThresholdFeedbackNode->SetData( workingimage->Clone() ); else m_ThresholdFeedbackNode->SetData( mitk::Image::New() ); int layer(50); m_NodeForThresholding->GetIntProperty("layer", layer); m_ThresholdFeedbackNode->SetIntProperty("layer", layer+1); if (DataStorage* ds = m_ToolManager->GetDataStorage()) { if (!ds->Exists(m_ThresholdFeedbackNode)) ds->Add( m_ThresholdFeedbackNode, m_OriginalImageNode ); } if (image.GetPointer() == originalImage.GetPointer()) { Image::StatisticsHolderPointer statistics = originalImage->GetStatistics(); m_SensibleMinimumThresholdValue = static_cast( statistics->GetScalarValueMin() ); m_SensibleMaximumThresholdValue = static_cast( statistics->GetScalarValueMax() ); } m_CurrentLowerThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue) / 3.0; m_CurrentUpperThresholdValue = 2.0 * m_CurrentLowerThresholdValue; IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue); ThresholdingValuesChanged.Send(m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue); } } } template static void ITKSetVolume( itk::Image* originalImage, mitk::Image* segmentation, unsigned int timeStep ) { segmentation->SetVolume( (void*) originalImage->GetPixelContainer()->GetBufferPointer(), timeStep); } void mitk::BinaryThresholdULTool::CreateNewSegmentationFromThreshold(DataNode* node) { if (node) { Image::Pointer feedBackImage = dynamic_cast( m_ThresholdFeedbackNode->GetData() ); if (feedBackImage.IsNotNull()) { // create a new image of the same dimensions and smallest possible pixel type DataNode::Pointer emptySegmentation = GetTargetSegmentationNode(); if (emptySegmentation) { // actually perform a thresholding and ask for an organ type for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep) { try { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( feedBackImage ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer feedBackImage3D = timeSelector->GetOutput(); if (feedBackImage3D->GetDimension() == 2) { AccessFixedDimensionByItk_2( feedBackImage3D, ITKSetVolume, 2, dynamic_cast(emptySegmentation->GetData()), timeStep ); } else { AccessFixedDimensionByItk_2( feedBackImage3D, ITKSetVolume, 3, dynamic_cast(emptySegmentation->GetData()), timeStep ); } } catch(...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); } } //since we are maybe working on a smaller image, pad it to the size of the original image if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, dynamic_cast (emptySegmentation->GetData())); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); emptySegmentation->SetData(padFilter->GetOutput()); } m_ToolManager->SetWorkingData( emptySegmentation ); m_ToolManager->GetWorkingData(0)->Modified(); } } } } void mitk::BinaryThresholdULTool::OnRoiDataChanged() { mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); mitk::Image::Pointer image = dynamic_cast (m_NodeForThresholding->GetData()); if (image.IsNull()) return; roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); tmpNode->SetData(roiFilter->GetOutput()); m_SensibleMinimumThresholdValue = static_cast( roiFilter->GetMinValue()); m_SensibleMaximumThresholdValue = static_cast( roiFilter->GetMaxValue()); m_NodeForThresholding = tmpNode; } else m_NodeForThresholding = m_OriginalImageNode; this->SetupPreviewNode(); this->UpdatePreview(); } template static void ITKThresholding( itk::Image* originalImage, mitk::Image* segmentation, double lower, double upper, unsigned int timeStep ) { typedef itk::Image ImageType; - typedef itk::Image SegmentationType; + typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(originalImage); filter->SetLowerThreshold(lower); filter->SetUpperThreshold(upper); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); segmentation->SetVolume( (void*) (filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); } void mitk::BinaryThresholdULTool::UpdatePreview() { mitk::Image::Pointer thresholdImage = dynamic_cast (m_NodeForThresholding->GetData()); mitk::Image::Pointer previewImage = dynamic_cast (m_ThresholdFeedbackNode->GetData()); if(thresholdImage && previewImage) { for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( thresholdImage ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer feedBackImage3D = timeSelector->GetOutput(); AccessByItk_n(feedBackImage3D, ITKThresholding, (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); } RenderingManager::GetInstance()->RequestUpdateAll(); } } diff --git a/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp index 73213e63d6..23995f8349 100644 --- a/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp @@ -1,190 +1,197 @@ /*=================================================================== 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 "mitkCorrectorTool2D.h" #include "mitkCorrectorAlgorithm.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkImageReadAccessor.h" #include "mitkAbstractTransformGeometry.h" +#include "mitkLabelSetImage.h" #include "mitkCorrectorTool2D.xpm" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, CorrectorTool2D, "Correction tool"); } mitk::CorrectorTool2D::CorrectorTool2D(int paintingPixelValue) :FeedbackContourTool("PressMoveRelease"), m_PaintingPixelValue(paintingPixelValue) { GetFeedbackContour()->SetClosed( false ); // don't close the contour to a polygon } mitk::CorrectorTool2D::~CorrectorTool2D() { } void mitk::CorrectorTool2D::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Move", OnMouseMoved); CONNECT_FUNCTION( "Release", OnMouseReleased); } const char** mitk::CorrectorTool2D::GetXPM() const { return mitkCorrectorTool2D_xpm; } us::ModuleResource mitk::CorrectorTool2D::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Correction_48x48.png"); return resource; } us::ModuleResource mitk::CorrectorTool2D::GetCursorIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Correction_Cursor_32x32.png"); return resource; } const char* mitk::CorrectorTool2D::GetName() const { return "Correction"; } void mitk::CorrectorTool2D::Activated() { Superclass::Activated(); } void mitk::CorrectorTool2D::Deactivated() { Superclass::Deactivated(); } bool mitk::CorrectorTool2D::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return false; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel* contour = FeedbackContourTool::GetFeedbackContour(); contour->Initialize(); contour->Expand(timestep + 1); contour->SetClosed(false, timestep); mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex( point, timestep ); FeedbackContourTool::SetFeedbackContourVisible(true); return true; } bool mitk::CorrectorTool2D::OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return false; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel* contour = FeedbackContourTool::GetFeedbackContour(); mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex( point, timestep ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::CorrectorTool2D::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return false; Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || !planeGeometry ) return false; const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || abstractTransformGeometry ) return false; // 2. Slice is known, now we try to get it as a 2D image and project the contour into index coordinates of this slice m_WorkingSlice = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image ); if ( m_WorkingSlice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return false; } int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel::Pointer singleTimestepContour = mitk::ContourModel::New(); mitk::ContourModel::VertexIterator it = FeedbackContourTool::GetFeedbackContour()->Begin(timestep); mitk::ContourModel::VertexIterator end = FeedbackContourTool::GetFeedbackContour()->End(timestep); while(it!=end) { singleTimestepContour->AddVertex((*it)->Coordinates); it++; } CorrectorAlgorithm::Pointer algorithm = CorrectorAlgorithm::New(); algorithm->SetInput( m_WorkingSlice ); algorithm->SetContour( singleTimestepContour ); + + mitk::LabelSetImage::Pointer labelSetImage = dynamic_cast(workingNode->GetData()); + if (labelSetImage.IsNotNull()) + { + algorithm->SetFillColor(labelSetImage->GetActiveLabel()->GetValue()); + } try { algorithm->UpdateLargestPossibleRegion(); } catch ( std::exception& e ) { MITK_ERROR << "Caught exception '" << e.what() << "'" << std::endl; } mitk::Image::Pointer resultSlice = mitk::Image::New(); resultSlice->Initialize(algorithm->GetOutput()); mitk::ImageReadAccessor imAccess(algorithm->GetOutput()); resultSlice->SetVolume(imAccess.GetData()); this->WriteBackSegmentationResult(positionEvent, resultSlice); return true; } \ No newline at end of file diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.h b/Modules/Segmentation/Interactions/mitkFastMarchingTool.h index 761545fcc5..f987cba635 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.h +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool.h @@ -1,184 +1,184 @@ /*=================================================================== 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 mitkFastMarchingTool_h_Included #define mitkFastMarchingTool_h_Included #include "mitkFeedbackContourTool.h" #include "mitkLegacyAdaptors.h" #include #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkToolCommand.h" #include "mitkPositionEvent.h" #include "mitkMessage.h" #include "itkImage.h" //itk filter #include "itkFastMarchingImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkSigmoidImageFilter.h" #include "itkCurvatureAnisotropicDiffusionImageFilter.h" namespace us { class ModuleResource; } namespace mitk { class StateMachineAction; class InteractionEvent; /** \brief FastMarching semgentation tool. The segmentation is done by setting one or more seed points on the image and adapting the time range and threshold. The pipeline is: Smoothing->GradientMagnitude->SigmoidFunction->FastMarching->Threshold The resulting binary image is seen as a segmentation of an object. For detailed documentation see ITK Software Guide section 9.3.1 Fast Marching Segmentation. */ class MITKSEGMENTATION_EXPORT FastMarchingTool : public FeedbackContourTool { mitkNewMessageMacro(Ready); public: mitkClassMacro(FastMarchingTool, FeedbackContourTool); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /* typedefs for itk pipeline */ typedef float InternalPixelType; typedef itk::Image< InternalPixelType, 2 > InternalImageType; - typedef unsigned char OutputPixelType; + typedef mitk::Tool::DefaultSegmentationDataType OutputPixelType; typedef itk::Image< OutputPixelType, 2 > OutputImageType; typedef itk::BinaryThresholdImageFilter< InternalImageType, OutputImageType > ThresholdingFilterType; typedef itk::CurvatureAnisotropicDiffusionImageFilter< InternalImageType, InternalImageType > SmoothingFilterType; typedef itk::GradientMagnitudeRecursiveGaussianImageFilter< InternalImageType, InternalImageType > GradientFilterType; typedef itk::SigmoidImageFilter< InternalImageType, InternalImageType > SigmoidFilterType; typedef itk::FastMarchingImageFilter< InternalImageType, InternalImageType > FastMarchingFilterType; typedef FastMarchingFilterType::NodeContainer NodeContainer; typedef FastMarchingFilterType::NodeType NodeType; /* icon stuff */ virtual const char** GetXPM() const override; virtual const char* GetName() const override; virtual us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; /// \brief Set parameter used in Threshold filter. void SetUpperThreshold(double); /// \brief Set parameter used in Threshold filter. void SetLowerThreshold(double); /// \brief Set parameter used in Fast Marching filter. void SetStoppingValue(double); /// \brief Set parameter used in Gradient Magnitude filter. void SetSigma(double); /// \brief Set parameter used in Fast Marching filter. void SetAlpha(double); /// \brief Set parameter used in Fast Marching filter. void SetBeta(double); /// \brief Adds the feedback image to the current working image. virtual void ConfirmSegmentation(); /// \brief Set the working time step. virtual void SetCurrentTimeStep(int t); /// \brief Clear all seed points. void ClearSeeds(); /// \brief Updates the itk pipeline and shows the result of FastMarching. void Update(); protected: FastMarchingTool(); virtual ~FastMarchingTool(); void ConnectActionsAndFunctions() override; //virtual float CanHandleEvent( StateEvent const *stateEvent) const; virtual void Activated() override; virtual void Deactivated() override; virtual void Initialize(); virtual void BuildITKPipeline(); /// \brief Add point action of StateMachine pattern virtual bool OnAddPoint ( StateMachineAction*, InteractionEvent* interactionEvent ); /// \brief Delete action of StateMachine pattern virtual bool OnDelete ( StateMachineAction*, InteractionEvent* interactionEvent ); /// \brief Reset all relevant inputs of the itk pipeline. void Reset(); mitk::ToolCommand::Pointer m_ProgressCommand; Image::Pointer m_ReferenceImage; Image::Pointer m_ReferenceImageSlice; bool m_NeedUpdate; int m_CurrentTimeStep; mitk::InteractionPositionEvent::Pointer m_PositionEvent; float m_LowerThreshold; //used in Threshold filter float m_UpperThreshold; //used in Threshold filter float m_StoppingValue; //used in Fast Marching filter float m_Sigma; //used in GradientMagnitude filter float m_Alpha; //used in Sigmoid filter float m_Beta; //used in Sigmoid filter NodeContainer::Pointer m_SeedContainer; //seed points for FastMarching InternalImageType::Pointer m_ReferenceImageSliceAsITK; //the reference image as itk::Image mitk::DataNode::Pointer m_ResultImageNode;//holds the result as a preview image mitk::DataNode::Pointer m_SeedsAsPointSetNode;//used to visualize the seed points mitk::PointSet::Pointer m_SeedsAsPointSet; ThresholdingFilterType::Pointer m_ThresholdFilter; SmoothingFilterType::Pointer m_SmoothFilter; GradientFilterType::Pointer m_GradientMagnitudeFilter; SigmoidFilterType::Pointer m_SigmoidFilter; FastMarchingFilterType::Pointer m_FastMarchingFilter; private: PlaneGeometry::Pointer m_WorkingPlane; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h b/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h index d80548d0bc..52da82660d 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h @@ -1,171 +1,171 @@ /*=================================================================== 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 mitkFastMarchingTool3D_h_Included #define mitkFastMarchingTool3D_h_Included #include "mitkAutoSegmentationTool.h" #include "mitkLegacyAdaptors.h" #include #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkPointSetInteractor.h" #include "mitkToolCommand.h" #include "mitkMessage.h" #include "itkImage.h" //itk filter #include "itkFastMarchingImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkSigmoidImageFilter.h" #include "itkCurvatureAnisotropicDiffusionImageFilter.h" namespace us { class ModuleResource; } namespace mitk { /** \brief FastMarching semgentation tool. The segmentation is done by setting one or more seed points on the image and adapting the time range and threshold. The pipeline is: Smoothing->GradientMagnitude->SigmoidFunction->FastMarching->Threshold The resulting binary image is seen as a segmentation of an object. For detailed documentation see ITK Software Guide section 9.3.1 Fast Marching Segmentation. */ class MITKSEGMENTATION_EXPORT FastMarchingTool3D : public AutoSegmentationTool { mitkNewMessageMacro(Ready); public: mitkClassMacro(FastMarchingTool3D, AutoSegmentationTool) itkFactorylessNewMacro(Self) itkCloneMacro(Self) /* typedefs for itk pipeline */ typedef float InternalPixelType; typedef itk::Image< InternalPixelType, 3 > InternalImageType; - typedef unsigned char OutputPixelType; + typedef mitk::Tool::DefaultSegmentationDataType OutputPixelType; typedef itk::Image< OutputPixelType, 3 > OutputImageType; typedef itk::BinaryThresholdImageFilter< InternalImageType, OutputImageType > ThresholdingFilterType; typedef itk::CurvatureAnisotropicDiffusionImageFilter< InternalImageType, InternalImageType > SmoothingFilterType; typedef itk::GradientMagnitudeRecursiveGaussianImageFilter< InternalImageType, InternalImageType > GradientFilterType; typedef itk::SigmoidImageFilter< InternalImageType, InternalImageType > SigmoidFilterType; typedef itk::FastMarchingImageFilter< InternalImageType, InternalImageType > FastMarchingFilterType; typedef FastMarchingFilterType::NodeContainer NodeContainer; typedef FastMarchingFilterType::NodeType NodeType; bool CanHandle(BaseData* referenceData) const override; /* icon stuff */ virtual const char** GetXPM() const override; virtual const char* GetName() const override; us::ModuleResource GetIconResource() const override; /// \brief Set parameter used in Threshold filter. void SetUpperThreshold(double); /// \brief Set parameter used in Threshold filter. void SetLowerThreshold(double); /// \brief Set parameter used in Fast Marching filter. void SetStoppingValue(double); /// \brief Set parameter used in Gradient Magnitude filter. void SetSigma(double); /// \brief Set parameter used in Fast Marching filter. void SetAlpha(double); /// \brief Set parameter used in Fast Marching filter. void SetBeta(double); /// \brief Adds the feedback image to the current working image. virtual void ConfirmSegmentation(); /// \brief Set the working time step. virtual void SetCurrentTimeStep(int t); /// \brief Clear all seed points. void ClearSeeds(); /// \brief Updates the itk pipeline and shows the result of FastMarching. void Update(); protected: FastMarchingTool3D(); virtual ~FastMarchingTool3D(); virtual void Activated() override; virtual void Deactivated() override; virtual void Initialize(); /// \brief Add point action of StateMachine pattern virtual void OnAddPoint (); /// \brief Delete action of StateMachine pattern virtual void OnDelete (); /// \brief Reset all relevant inputs of the itk pipeline. void Reset(); mitk::ToolCommand::Pointer m_ProgressCommand; Image::Pointer m_ReferenceImage; bool m_NeedUpdate; int m_CurrentTimeStep; float m_LowerThreshold; //used in Threshold filter float m_UpperThreshold; //used in Threshold filter float m_StoppingValue; //used in Fast Marching filter float m_Sigma; //used in GradientMagnitude filter float m_Alpha; //used in Sigmoid filter float m_Beta; //used in Sigmoid filter NodeContainer::Pointer m_SeedContainer; //seed points for FastMarching InternalImageType::Pointer m_ReferenceImageAsITK; //the reference image as itk::Image mitk::DataNode::Pointer m_ResultImageNode;//holds the result as a preview image mitk::DataNode::Pointer m_SeedsAsPointSetNode;//used to visualize the seed points mitk::PointSet::Pointer m_SeedsAsPointSet; mitk::PointSetInteractor::Pointer m_SeedPointInteractor; unsigned int m_PointSetAddObserverTag; unsigned int m_PointSetRemoveObserverTag; ThresholdingFilterType::Pointer m_ThresholdFilter; SmoothingFilterType::Pointer m_SmoothFilter; GradientFilterType::Pointer m_GradientMagnitudeFilter; SigmoidFilterType::Pointer m_SigmoidFilter; FastMarchingFilterType::Pointer m_FastMarchingFilter; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp b/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp index 06912b5d99..bccb98d5b9 100644 --- a/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp +++ b/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp @@ -1,301 +1,306 @@ /*=================================================================== 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. ===================================================================*/ // MITK #include "mitkOtsuTool3D.h" #include "mitkToolManager.h" #include "mitkRenderingManager.h" #include #include #include #include #include #include #include "mitkOtsuSegmentationFilter.h" -#include "mitkImage.h" +#include "mitkLabelSetImage.h" #include "mitkImageAccessByItk.h" // ITK #include #include #include // us #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, OtsuTool3D, "Otsu Segmentation"); } mitk::OtsuTool3D::OtsuTool3D() { } mitk::OtsuTool3D::~OtsuTool3D() { } void mitk::OtsuTool3D::Activated() { if (m_ToolManager) { m_OriginalImage = dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData()); m_BinaryPreviewNode = mitk::DataNode::New(); m_BinaryPreviewNode->SetName("Binary_Preview"); m_BinaryPreviewNode->SetProperty( "color", ColorProperty::New(0.0, 1.0, 0.0) ); m_BinaryPreviewNode->SetProperty( "opacity", FloatProperty::New(0.3) ); //m_BinaryPreviewNode->SetBoolProperty("helper object", true); //m_BinaryPreviewNode->SetProperty("binary", mitk::BoolProperty::New(true)); m_ToolManager->GetDataStorage()->Add( this->m_BinaryPreviewNode ); m_MultiLabelResultNode = mitk::DataNode::New(); m_MultiLabelResultNode->SetName("Otsu_Preview"); //m_MultiLabelResultNode->SetBoolProperty("helper object", true); m_MultiLabelResultNode->SetVisibility(true); m_MaskedImagePreviewNode = mitk::DataNode::New(); m_MaskedImagePreviewNode->SetName("Volume_Preview"); //m_MultiLabelResultNode->SetBoolProperty("helper object", true); m_MaskedImagePreviewNode->SetVisibility(false); m_ToolManager->GetDataStorage()->Add( this->m_MultiLabelResultNode ); } } void mitk::OtsuTool3D::Deactivated() { m_ToolManager->GetDataStorage()->Remove( this->m_MultiLabelResultNode ); m_MultiLabelResultNode = NULL; m_ToolManager->GetDataStorage()->Remove( this->m_BinaryPreviewNode ); m_BinaryPreviewNode = NULL; m_ToolManager->GetDataStorage()->Remove( this->m_MaskedImagePreviewNode); m_MaskedImagePreviewNode = NULL; } const char** mitk::OtsuTool3D::GetXPM() const { return NULL; } us::ModuleResource mitk::OtsuTool3D::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Otsu_48x48.png"); return resource; } void mitk::OtsuTool3D::RunSegmentation(int regions, bool useValley, int numberOfBins) { //this->m_OtsuSegmentationDialog->setCursor(Qt::WaitCursor); int numberOfThresholds = regions - 1; unsigned int timestep = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->GetPos(); mitk::Image::Pointer image3D = Get3DImage(m_OriginalImage, timestep); mitk::OtsuSegmentationFilter::Pointer otsuFilter = mitk::OtsuSegmentationFilter::New(); otsuFilter->SetNumberOfThresholds( numberOfThresholds ); otsuFilter->SetValleyEmphasis( useValley ); otsuFilter->SetNumberOfBins( numberOfBins ); otsuFilter->SetInput( image3D ); try { otsuFilter->Update(); } catch( ... ) { mitkThrow() << "itkOtsuFilter error (image dimension must be in {2, 3} and image must not be RGB)"; } m_ToolManager->GetDataStorage()->Remove( this->m_MultiLabelResultNode ); m_MultiLabelResultNode = NULL; m_MultiLabelResultNode = mitk::DataNode::New(); m_MultiLabelResultNode->SetName("Otsu_Preview"); m_MultiLabelResultNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add( this->m_MultiLabelResultNode ); m_MultiLabelResultNode->SetOpacity(1.0); - this->m_MultiLabelResultNode->SetData( otsuFilter->GetOutput() ); + mitk::LabelSetImage::Pointer resultImage = mitk::LabelSetImage::New(); + resultImage->InitializeByLabeledImage( otsuFilter->GetOutput() ); + this->m_MultiLabelResultNode->SetData(resultImage); m_MultiLabelResultNode->SetProperty("binary", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); renderingMode->SetValue( mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR ); m_MultiLabelResultNode->SetProperty("Image Rendering.Mode", renderingMode); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); mitk::LookupTableProperty::Pointer prop = mitk::LookupTableProperty::New(lut); vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetHueRange(1.0, 0.0); lookupTable->SetSaturationRange(1.0, 1.0); lookupTable->SetValueRange(1.0, 1.0); lookupTable->SetTableRange(-1.0, 1.0); lookupTable->Build(); lut->SetVtkLookupTable(lookupTable); prop->SetLookupTable(lut); m_MultiLabelResultNode->SetProperty("LookupTable",prop); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetRangeMinMax(0, numberOfThresholds + 1); levWinProp->SetLevelWindow( levelwindow ); m_MultiLabelResultNode->SetProperty( "levelwindow", levWinProp ); //m_BinaryPreviewNode->SetVisibility(false); // m_MultiLabelResultNode->SetVisibility(true); //this->m_OtsuSegmentationDialog->setCursor(Qt::ArrowCursor); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::OtsuTool3D::ConfirmSegmentation() { - GetTargetSegmentationNode()->SetData(dynamic_cast(m_BinaryPreviewNode->GetData())); + mitk::LabelSetImage::Pointer resultImage = mitk::LabelSetImage::New(); + resultImage->InitializeByLabeledImage(dynamic_cast(m_BinaryPreviewNode->GetData())); + GetTargetSegmentationNode()->SetData(resultImage); + m_ToolManager->ActivateTool(-1); } void mitk::OtsuTool3D::UpdateBinaryPreview(std::vector regionIDs) { m_MultiLabelResultNode->SetVisibility(false); mitk::Image::Pointer multiLabelSegmentation = dynamic_cast(m_MultiLabelResultNode->GetData()); AccessByItk_1( multiLabelSegmentation, CalculatePreview, regionIDs); } template< typename TPixel, unsigned int VImageDimension> void mitk::OtsuTool3D::CalculatePreview(itk::Image< TPixel, VImageDimension>* itkImage, std::vector regionIDs) { typedef itk::Image< TPixel, VImageDimension > InputImageType; - typedef itk::Image< unsigned char, VImageDimension > OutputImageType; + typedef itk::Image< mitk::Tool::DefaultSegmentationDataType, VImageDimension > OutputImageType; typedef itk::BinaryThresholdImageFilter< InputImageType, OutputImageType > FilterType; typename FilterType::Pointer filter = FilterType::New(); //InputImageType::Pointer itkImage; typename OutputImageType::Pointer itkBinaryTempImage1; typename OutputImageType::Pointer itkBinaryTempImage2; typename OutputImageType::Pointer itkBinaryResultImage; //mitk::Image::Pointer multiLabelSegmentation = dynamic_cast(m_MultiLabelResultNode->GetData()); //mitk::CastToItkImage(multiLabelSegmentation, itkImage); filter->SetInput(itkImage); filter->SetLowerThreshold(regionIDs[0]); filter->SetUpperThreshold(regionIDs[0]); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); itkBinaryTempImage2 = filter->GetOutput(); typename itk::OrImageFilter::Pointer orFilter = itk::OrImageFilter::New(); // if more than one region id is used compute the union of all given binary regions for (std::vector::iterator it = regionIDs.begin() ; it != regionIDs.end(); ++it) { filter->SetLowerThreshold(*it); filter->SetUpperThreshold(*it); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); itkBinaryTempImage1 = filter->GetOutput(); orFilter->SetInput1(itkBinaryTempImage1); orFilter->SetInput2(itkBinaryTempImage2); orFilter->UpdateLargestPossibleRegion(); itkBinaryResultImage = orFilter->GetOutput(); itkBinaryTempImage2 = itkBinaryResultImage; } //---------------------------------------------------------------------------------------------------- mitk::Image::Pointer binarySegmentation; mitk::CastToMitkImage( itkBinaryResultImage, binarySegmentation); m_BinaryPreviewNode->SetData(binarySegmentation); m_BinaryPreviewNode->SetVisibility(true); m_BinaryPreviewNode->SetProperty("outline binary", mitk::BoolProperty::New(false)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } //void mitk::OtsuTool3D::UpdateBinaryPreview(int regionID) //{ // m_MultiLabelResultNode->SetVisibility(false); // //pixel with regionID -> binary image // const unsigned short dim = 3; // typedef unsigned char PixelType; // // typedef itk::Image< PixelType, dim > InputImageType; // typedef itk::Image< PixelType, dim > OutputImageType; // // typedef itk::BinaryThresholdImageFilter< InputImageType, OutputImageType > FilterType; // // FilterType::Pointer filter = FilterType::New(); // // InputImageType::Pointer itkImage; // // mitk::Image::Pointer multiLabelSegmentation = dynamic_cast(m_MultiLabelResultNode->GetData()); // mitk::CastToItkImage(multiLabelSegmentation, itkImage); // // filter->SetInput(itkImage); // filter->SetLowerThreshold(regionID); // filter->SetUpperThreshold(regionID); // filter->SetInsideValue(1); // filter->SetOutsideValue(0); // filter->Update(); // mitk::Image::Pointer binarySegmentation; // mitk::CastToMitkImage( filter->GetOutput(), binarySegmentation); // m_BinaryPreviewNode->SetData(binarySegmentation); // m_BinaryPreviewNode->SetVisibility(true); // m_BinaryPreviewNode->SetProperty("outline binary", mitk::BoolProperty::New(false)); // // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); //} const char* mitk::OtsuTool3D::GetName() const { return "Otsu"; } void mitk::OtsuTool3D::UpdateVolumePreview(bool volumeRendering) { if (volumeRendering) { m_MaskedImagePreviewNode->SetBoolProperty("volumerendering", true); m_MaskedImagePreviewNode->SetBoolProperty("volumerendering.uselod", true); } else { m_MaskedImagePreviewNode->SetBoolProperty("volumerendering", false); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::OtsuTool3D::ShowMultiLabelResultNode(bool show) { m_MultiLabelResultNode->SetVisibility(show); m_BinaryPreviewNode->SetVisibility(!show); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } int mitk::OtsuTool3D::GetNumberOfBins() { ScalarType min = m_OriginalImage.GetPointer()->GetStatistics()->GetScalarValueMin(); ScalarType max = m_OriginalImage.GetPointer()->GetStatistics()->GetScalarValueMaxNoRecompute(); return static_cast(max-min) + 1; } diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp index ac510c6688..1bdd0dad65 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp @@ -1,246 +1,249 @@ /*=================================================================== 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 "mitkPickingTool.h" #include "mitkToolManager.h" #include "mitkProperties.h" // us #include #include #include #include #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include +#include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PickingTool, "PickingTool"); } mitk::PickingTool::PickingTool() : m_WorkingData(NULL) { m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->GetPropertyList()->SetProperty("name", mitk::StringProperty::New("Picking_Seedpoint")); m_PointSetNode->GetPropertyList()->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PointSet = mitk::PointSet::New(); m_PointSetNode->SetData(m_PointSet); m_SeedPointInteractor = mitk::SinglePointDataInteractor::New(); m_SeedPointInteractor->LoadStateMachine("PointSet.xml"); m_SeedPointInteractor->SetEventConfig("PointSetConfig.xml"); m_SeedPointInteractor->SetDataNode(m_PointSetNode); //Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &mitk::PickingTool::OnPointAdded); m_PointSetAddObserverTag = m_PointSet->AddObserver( mitk::PointSetAddEvent(), pointAddedCommand); //create new node for picked region m_ResultNode = mitk::DataNode::New(); // set some properties m_ResultNode->SetProperty("name", mitk::StringProperty::New("result")); m_ResultNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ResultNode->SetProperty("color", mitk::ColorProperty::New(0, 1, 0)); m_ResultNode->SetProperty("layer", mitk::IntProperty::New(1)); m_ResultNode->SetProperty("opacity", mitk::FloatProperty::New(0.33f)); } mitk::PickingTool::~PickingTool() { m_PointSet->RemoveObserver(m_PointSetAddObserverTag); } const char** mitk::PickingTool::GetXPM() const { return NULL; } const char* mitk::PickingTool::GetName() const { return "Picking"; } us::ModuleResource mitk::PickingTool::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Pick_48x48.png"); return resource; } void mitk::PickingTool::Activated() { DataStorage* dataStorage = this->GetDataStorage(); m_WorkingData = this->GetWorkingData(); //add to datastorage and enable interaction if (!dataStorage->Exists(m_PointSetNode)) dataStorage->Add(m_PointSetNode, m_WorkingData); // now add result to data tree dataStorage->Add(m_ResultNode, m_WorkingData); } void mitk::PickingTool::Deactivated() { m_PointSet->Clear(); //remove from data storage and disable interaction GetDataStorage()->Remove(m_PointSetNode); GetDataStorage()->Remove( m_ResultNode); } mitk::DataNode* mitk::PickingTool::GetReferenceData(){ return this->m_ToolManager->GetReferenceData(0); } mitk::DataStorage* mitk::PickingTool::GetDataStorage(){ return this->m_ToolManager->GetDataStorage(); } mitk::DataNode* mitk::PickingTool::GetWorkingData(){ return this->m_ToolManager->GetWorkingData(0); } mitk::DataNode::Pointer mitk::PickingTool::GetPointSetNode() { return m_PointSetNode; } void mitk::PickingTool::OnPointAdded() { if (m_WorkingData != this->GetWorkingData()) { DataStorage* dataStorage = this->GetDataStorage(); if (dataStorage->Exists(m_PointSetNode)) { dataStorage->Remove(m_PointSetNode); dataStorage->Add(m_PointSetNode, this->GetWorkingData()); } if (dataStorage->Exists(m_ResultNode)) { dataStorage->Remove(m_ResultNode); dataStorage->Add(m_ResultNode, this->GetWorkingData()); } m_WorkingData = this->GetWorkingData(); } //Perform region growing/picking int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); mitk::PointSet::PointType seedPoint = m_PointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); //as we want to pick a region from our segmentation image use the working data from ToolManager mitk::Image::Pointer orgImage = dynamic_cast (m_ToolManager->GetWorkingData(0)->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { //there may be 4D segmentation data even though we currently don't support that mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* timedImage = timeSelector->GetOutput(); AccessByItk_2( timedImage , StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); } this->m_PointSet->Clear(); } } template void mitk::PickingTool::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // convert world coordinates to image indices IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex); //perform region growing in desired segmented region regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); regionGrower->SetLower( 1 ); regionGrower->SetUpper( 255 ); try { regionGrower->Update(); } catch(const itk::ExceptionObject&) { return; // can't work } catch( ... ) { return; } //Store result and preview mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput(),imageGeometry)->Clone(); + mitk::LabelSetImage::Pointer resultLabelSetImage = mitk::LabelSetImage::New(); + resultLabelSetImage->InitializeByLabeledImage(resultImage); - m_ResultNode->SetData( resultImage ); + m_ResultNode->SetData(resultLabelSetImage); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PickingTool::ConfirmSegmentation() { mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetProperty("name", mitk::StringProperty::New(m_WorkingData->GetName() + "_picked")); float rgb[3] = { 1.0f, 0.0f, 0.0f }; m_WorkingData->GetColor(rgb); newNode->SetProperty("color", mitk::ColorProperty::New(rgb)); float opacity = 1.0f; m_WorkingData->GetOpacity(opacity, NULL); newNode->SetProperty("opacity", mitk::FloatProperty::New(opacity)); newNode->SetData(m_ResultNode->GetData()); GetDataStorage()->Add(newNode, this->GetReferenceData()); m_WorkingData->SetVisibility(false); m_ResultNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp index e6ce317965..c4f259ebe2 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp @@ -1,442 +1,442 @@ /*=================================================================== 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 "mitkRegionGrow3DTool.h" #include "mitkToolManager.h" #include "mitkRenderingManager.h" #include "mitkLevelWindowProperty.h" #include "mitkPointSetInteractor.h" #include "mitkGlobalInteraction.h" #include "mitkITKImageImport.h" #include "itkImage.h" #include "itkBinaryThresholdImageFilter.h" #include "itkConnectedAdaptiveThresholdImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkImageStatisticsHolder.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkRegionGrow3DTool.xpm" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, RegionGrow3DTool, "RegionGrower 3D"); } mitk::RegionGrow3DTool::RegionGrow3DTool(): Tool("PressMoveRelease"), m_LowerThreshold(-5000), m_UpperThreshold(5000), m_CurrentRGDirectionIsUpwards(false) { CONNECT_FUNCTION( "Release", OnMouseReleased); m_FeedbackNode = DataNode::New(); m_FeedbackNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", IntProperty::New( 100 ) ); m_FeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(100, 1) ) ); m_FeedbackNode->SetProperty( "name", StringProperty::New("failsafe region grow feedback") ); m_FeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_FeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); m_FeedbackNode->SetVisibility(false); m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->SetName("regiongrow3d pointset"); m_PointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PointSetNode->SetProperty("layer", mitk::IntProperty::New(2)); mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); m_PointSetNode->SetData(pointSet); } mitk::RegionGrow3DTool::~RegionGrow3DTool() { } bool mitk::RegionGrow3DTool::OnMouseReleased( StateMachineAction*, InteractionEvent* /*interactionEvent*/ ) { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNotNull()) { mitk::PointSet::Pointer pointSet = dynamic_cast (m_PointSetNode->GetData()); if (pointSet.IsNotNull()) { if (pointSet->GetSize() == 1) { //check if we have a valid picture this->RunSegmentation(); SeedButtonToggled.Send(false); } } } return true; } void mitk::RegionGrow3DTool::Activated() { if (m_ToolManager) { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::RegionGrow3DTool::UpdatePreview); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeToProceed = m_OriginalImageNode; if (m_NodeToProceed.IsNotNull()) { SetupPreviewNodeFor(m_NodeToProceed); mitk::Image::Pointer image = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { Image::StatisticsHolderPointer statistics = image->GetStatistics(); m_RoiMin = statistics->GetScalarValueMin(); m_RoiMax = statistics->GetScalarValueMax(); } } else m_ToolManager->ActivateTool(-1); } } void mitk::RegionGrow3DTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::RegionGrow3DTool::UpdatePreview); if (mitk::DataStorage* ds = m_ToolManager->GetDataStorage()) { ds->Remove(m_PointSetNode); ds->Remove(m_FeedbackNode); } m_FeedbackNode->SetData(nullptr); m_FeedbackNode->SetLevelWindow(LevelWindow()); m_FeedbackNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } const char* mitk::RegionGrow3DTool::GetName() const { return "RegionGrower 3D"; } const char** mitk::RegionGrow3DTool::GetXPM() const { return mitkRegionGrow3DTool_xpm; } void mitk::RegionGrow3DTool::SetSeedPoint(bool toggled) { if (!m_ToolManager->GetDataStorage()->Exists(m_PointSetNode)) //no pointSet present m_ToolManager->GetDataStorage()->Add(m_PointSetNode, m_OriginalImageNode); //add Interactor if there is none and add it to GlobalInteraction if (toggled == true) // button is down { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNull()) { //create a new interactor and add it to node interactor = mitk::PointSetInteractor::New("singlepointinteractorwithoutshiftclick", m_PointSetNode, 1); } mitk::GlobalInteraction::GetInstance()->AddInteractor(interactor); } else { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNotNull()) { m_PointSetNode->SetInteractor(NULL); mitk::GlobalInteraction::GetInstance()->RemoveInteractor(interactor); } } } void mitk::RegionGrow3DTool::RunSegmentation() { //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (m_PointSetNode->GetData()); if (seedPointSet.IsNull()) { return; } if (!(seedPointSet->GetSize() > 0)) { return; } mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet()->GetPoints()->Begin().Value(); mitk::Image::Pointer image = dynamic_cast (m_NodeToProceed->GetData()); if (image.IsNotNull()) { m_LowerThreshold = static_cast (m_RoiMin); m_UpperThreshold = static_cast (m_RoiMax); AccessByItk_2(image, StartRegionGrowing, image->GetGeometry(), seedPoint); } } template void mitk::RegionGrow3DTool::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); if ( !imageGeometry->IsInside(seedPoint) ) { return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices //int seedValue = itkImage->GetPixel(seedIndex); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); regionGrower->SetLower( m_LowerThreshold ); regionGrower->SetUpper( m_UpperThreshold ); regionGrower->SetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); try { regionGrower->Update(); } catch( ... ) { MITK_ERROR << "Something went wrong!" << endl; return; } m_SeedpointValue = regionGrower->GetSeedpointValue(); //initialize slider if(m_CurrentRGDirectionIsUpwards) { UpperThresholdValueChanged.Send(m_UpperThreshold); LowerThresholdValueChanged.Send(m_SeedpointValue); } else { UpperThresholdValueChanged.Send(m_SeedpointValue); LowerThresholdValueChanged.Send(m_LowerThreshold); } m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); m_FeedbackNode->SetData( resultImage ); m_FeedbackNode->SetVisibility(true); InitializeLevelWindow(); } void mitk::RegionGrow3DTool::InitializeLevelWindow() { mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow(tempLevelWindow, NULL, "levelWindow"); mitk::ScalarType* level = new mitk::ScalarType(0.5); mitk::ScalarType* window = new mitk::ScalarType(1); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UpperThreshold - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LowerThreshold; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); tempLevelWindow.SetLevelWindow(*level, *window); m_FeedbackNode->SetLevelWindow(tempLevelWindow, NULL, "levelWindow"); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { SliderValueChanged.Send(m_SeedpointValue + m_DetectedLeakagePoint -1); } else { SliderValueChanged.Send(m_SeedpointValue - m_DetectedLeakagePoint +1); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::ChangeLevelWindow(int value) { if (m_FeedbackNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow(tempLevelWindow, NULL, "levelWindow"); //get the levelWindow associated with the preview mitk::ScalarType level;// = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType* window = new mitk::ScalarType(1); //adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UpperThreshold - value + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = value - m_LowerThreshold +0.5; tempLevelWindow.SetLevelWindow(level, *window); } m_FeedbackNode->SetLevelWindow(tempLevelWindow, NULL, "levelWindow"); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::ConfirmSegmentation( std::string name, mitk::Color color) { mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetColor(color); new_node->SetName(name); new_node->SetProperty("binary", mitk::BoolProperty::New("true")); mitk::Image* image = dynamic_cast (m_FeedbackNode->GetData()); mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow( tempLevelWindow, NULL, "levelWindow"); int upperThresholdLabeledImage = (short int) tempLevelWindow.GetRangeMax(); int lowerThresholdLabeledImage = (short int) tempLevelWindow.GetLowerWindowBound() + 1; typedef itk::Image InputImageType; - typedef itk::Image SegmentationType; + typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); InputImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); filter->SetInput(itkImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->SetUpperThreshold(upperThresholdLabeledImage); filter->SetLowerThreshold(lowerThresholdLabeledImage); filter->Update(); mitk::Image::Pointer new_image = mitk::Image::New(); mitk::CastToMitkImage(filter->GetOutput(), new_image); //pad to original size if (m_OriginalImageNode.GetPointer() != m_NodeToProceed.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, new_image); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); new_image = padFilter->GetOutput(); } new_node->SetData(new_image); m_ToolManager->GetDataStorage()->Add(new_node); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::RegionGrow3DTool::CancelSegmentation() { m_ToolManager->ActivateTool(-1); } void mitk::RegionGrow3DTool::SetupPreviewNodeFor( DataNode* nodeToProceed) { if (nodeToProceed) { Image::Pointer image = dynamic_cast( nodeToProceed->GetData() ); if (image.IsNotNull()) { m_FeedbackNode->SetData( image ); int layer(50); nodeToProceed->GetIntProperty("layer", layer); m_FeedbackNode->SetIntProperty("layer", layer+1); m_FeedbackNode->SetLevelWindow(LevelWindow()); if (DataStorage* storage = m_ToolManager->GetDataStorage()) { if (storage->Exists(m_FeedbackNode)) storage->Remove(m_FeedbackNode); storage->Add( m_FeedbackNode, nodeToProceed ); } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::UpdatePreview() { typedef itk::Image ItkImageType; typedef itk::Image ItkMaskType; mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNull()) { this->SetupPreviewNodeFor(m_OriginalImageNode); m_NodeToProceed = m_OriginalImageNode; mitk::Image::Pointer image = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { Image::StatisticsHolderPointer statistics = image->GetStatistics(); m_RoiMin = statistics->GetScalarValueMin(); m_RoiMax = statistics->GetScalarValueMax(); } return; } mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); roiFilter->SetInput(dynamic_cast (m_NodeToProceed->GetData())); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); mitk::Image::Pointer tmpImage = roiFilter->GetOutput(); new_node->SetData(tmpImage); m_RoiMax = roiFilter->GetMaxValue(); m_RoiMin = roiFilter->GetMinValue(); this->SetupPreviewNodeFor(new_node); m_NodeToProceed = new_node; //this->RunSegmentation(); } void mitk::RegionGrow3DTool::SetCurrentRGDirectionIsUpwards(bool flag) { m_CurrentRGDirectionIsUpwards = flag; } diff --git a/Modules/Segmentation/Interactions/mitkTool.cpp b/Modules/Segmentation/Interactions/mitkTool.cpp index 1cc343296b..06816d753c 100644 --- a/Modules/Segmentation/Interactions/mitkTool.cpp +++ b/Modules/Segmentation/Interactions/mitkTool.cpp @@ -1,255 +1,274 @@ /*=================================================================== 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 "mitkTool.h" #include "mitkProperties.h" +#include "mitkImageReadAccessor.h" #include "mitkImageWriteAccessor.h" +#include "mitkLabelSetImage.h" #include "mitkLevelWindowProperty.h" +#include "mitkLookupTableProperty.h" +#include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" -#include "mitkImageReadAccessor.h" // us -#include #include +#include +// itk #include mitk::Tool::Tool(const char* type) : m_PredicateImages(NodePredicateDataType::New("Image")) // for reference images , m_PredicateDim3(NodePredicateDimension::New(3, 1)) , m_PredicateDim4(NodePredicateDimension::New(4, 1)) , m_PredicateDimension( mitk::NodePredicateOr::New(m_PredicateDim3, m_PredicateDim4) ) , m_PredicateImage3D( NodePredicateAnd::New(m_PredicateImages, m_PredicateDimension) ) , m_PredicateBinary(NodePredicateProperty::New("binary", BoolProperty::New(true))) , m_PredicateNotBinary( NodePredicateNot::New(m_PredicateBinary) ) , m_PredicateSegmentation(NodePredicateProperty::New("segmentation", BoolProperty::New(true))) , m_PredicateNotSegmentation( NodePredicateNot::New(m_PredicateSegmentation) ) , m_PredicateHelper(NodePredicateProperty::New("helper object", BoolProperty::New(true))) , m_PredicateNotHelper( NodePredicateNot::New(m_PredicateHelper) ) , m_PredicateImageColorful( NodePredicateAnd::New(m_PredicateNotBinary, m_PredicateNotSegmentation) ) , m_PredicateImageColorfulNotHelper( NodePredicateAnd::New(m_PredicateImageColorful, m_PredicateNotHelper) ) , m_PredicateReference( NodePredicateAnd::New(m_PredicateImage3D, m_PredicateImageColorfulNotHelper) ) , m_IsSegmentationPredicate(NodePredicateAnd::New(NodePredicateOr::New(m_PredicateBinary, m_PredicateSegmentation), m_PredicateNotHelper)) , m_InteractorType( type ) { } mitk::Tool::~Tool() { } bool mitk::Tool::CanHandle(BaseData*) const { return true; } void mitk::Tool::InitializeStateMachine() { if (m_InteractorType.empty()) return; m_InteractorType += ".xml"; try { LoadStateMachine( m_InteractorType, us::GetModuleContext()->GetModule() ); SetEventConfig( "SegmentationToolsConfig.xml", us::GetModuleContext()->GetModule() ); } catch( const std::exception& e ) { MITK_ERROR << "Could not load statemachine pattern " << m_InteractorType << " with exception: " << e.what(); } } void mitk::Tool::Notify( InteractionEvent* interactionEvent, bool isHandled ) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if ( !isHandled ) { this->HandleEvent(interactionEvent, NULL); } } void mitk::Tool::ConnectActionsAndFunctions() { } bool mitk::Tool::FilterEvents(InteractionEvent* , DataNode* ) { return true; } const char* mitk::Tool::GetGroup() const { return "default"; } void mitk::Tool::SetToolManager(ToolManager* manager) { m_ToolManager = manager; } void mitk::Tool::Activated() { } void mitk::Tool::Deactivated() { // ToDo: reactivate this feature! //StateMachine::ResetStatemachineToStartState(); // forget about the past } itk::Object::Pointer mitk::Tool::GetGUI(const std::string& toolkitPrefix, const std::string& toolkitPostfix) { itk::Object::Pointer object; std::string classname = this->GetNameOfClass(); std::string guiClassname = toolkitPrefix + classname + toolkitPostfix; std::list allGUIs = itk::ObjectFactoryBase::CreateAllInstance(guiClassname.c_str()); for( std::list::iterator iter = allGUIs.begin(); iter != allGUIs.end(); ++iter ) { if (object.IsNull()) { object = dynamic_cast( iter->GetPointer() ); } else { MITK_ERROR << "There is more than one GUI for " << classname << " (several factories claim ability to produce a " << guiClassname << " ) " << std::endl; return NULL; // people should see and fix this error } } return object; } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetReferenceDataPreference() const { return m_PredicateReference.GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetWorkingDataPreference() const { return m_IsSegmentationPredicate.GetPointer(); } + mitk::DataNode::Pointer mitk::Tool::CreateEmptySegmentationNode( Image* original, const std::string& organName, const mitk::Color& color ) { // we NEED a reference image for size etc. if (!original) return NULL; // actually create a new empty segmentation PixelType pixelType(mitk::MakeScalarPixelType() ); - Image::Pointer segmentation = Image::New(); + LabelSetImage::Pointer segmentation = LabelSetImage::New(); if (original->GetDimension() == 2) { const unsigned int dimensions[] = { original->GetDimension(0), original->GetDimension(1), 1 }; segmentation->Initialize(pixelType, 3, dimensions); + segmentation->AddLayer(); } else { - segmentation->Initialize(pixelType, original->GetDimension(), original->GetDimensions()); + segmentation->Initialize(original); } - unsigned int byteSize = sizeof(DefaultSegmentationDataType); + mitk::Label::Pointer label = mitk::Label::New(); + label->SetName(organName); + label->SetColor(color); + label->SetValue(1); + segmentation->GetActiveLabelSet()->AddLabel(label); + segmentation->GetActiveLabelSet()->SetActiveLabel(1); + + unsigned int byteSize = sizeof(mitk::Label::PixelType); if(segmentation->GetDimension() < 4) { for (unsigned int dim = 0; dim < segmentation->GetDimension(); ++dim) { byteSize *= segmentation->GetDimension(dim); } - mitk::ImageWriteAccessor writeAccess(segmentation, segmentation->GetVolumeData(0)); + mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(0)); memset( writeAccess.GetData(), 0, byteSize ); } else - {//if we have a time-resolved image we need to set memory to 0 for each time step + { + //if we have a time-resolved image we need to set memory to 0 for each time step for (unsigned int dim = 0; dim < 3; ++dim) { byteSize *= segmentation->GetDimension(dim); } for( unsigned int volumeNumber = 0; volumeNumber < segmentation->GetDimension(3); volumeNumber++) { - mitk::ImageWriteAccessor writeAccess(segmentation, segmentation->GetVolumeData(volumeNumber)); + mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(volumeNumber)); memset( writeAccess.GetData(), 0, byteSize ); } } if (original->GetTimeGeometry() ) { TimeGeometry::Pointer originalGeometry = original->GetTimeGeometry()->Clone(); segmentation->SetTimeGeometry( originalGeometry ); } else { Tool::ErrorMessage("Original image does not have a 'Time sliced geometry'! Cannot create a segmentation."); return NULL; } return CreateSegmentationNode( segmentation, organName, color ); } mitk::DataNode::Pointer mitk::Tool::CreateSegmentationNode( Image* image, const std::string& organName, const mitk::Color& color ) { if (!image) return NULL; // decorate the datatreenode with some properties DataNode::Pointer segmentationNode = DataNode::New(); segmentationNode->SetData( image ); // name segmentationNode->SetProperty( "name", StringProperty::New( organName ) ); // visualization properties segmentationNode->SetProperty( "binary", BoolProperty::New(true) ); - segmentationNode->SetProperty( "color", ColorProperty::New(color) ); +// segmentationNode->SetProperty( "color", ColorProperty::New(color) ); + mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); + lut->SetType(mitk::LookupTable::MULTILABEL); + mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); + lutProp->SetLookupTable(lut); + segmentationNode->SetProperty("LookupTable", lutProp); segmentationNode->SetProperty( "texture interpolation", BoolProperty::New(false) ); segmentationNode->SetProperty( "layer", IntProperty::New(10) ); segmentationNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(0.5, 1) ) ); segmentationNode->SetProperty( "opacity", FloatProperty::New(0.3) ); segmentationNode->SetProperty( "segmentation", BoolProperty::New(true) ); segmentationNode->SetProperty( "reslice interpolation", VtkResliceInterpolationProperty::New() ); // otherwise -> segmentation appears in 2 slices sometimes (only visual effect, not different data) // For MITK-3M3 release, the volume of all segmentations should be shown segmentationNode->SetProperty( "showVolume", BoolProperty::New( true ) ); return segmentationNode; } us::ModuleResource mitk::Tool::GetIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } us::ModuleResource mitk::Tool::GetCursorIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } diff --git a/Modules/Segmentation/Interactions/mitkTool.h b/Modules/Segmentation/Interactions/mitkTool.h index aa8d4c8c60..f323a264eb 100644 --- a/Modules/Segmentation/Interactions/mitkTool.h +++ b/Modules/Segmentation/Interactions/mitkTool.h @@ -1,254 +1,255 @@ /*=================================================================== 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 mitkTool_h_Included #define mitkTool_h_Included #include "mitkCommon.h" #include #include "mitkStateMachine.h" #include "mitkToolEvents.h" #include "itkObjectFactoryBase.h" #include "itkVersion.h" #include "mitkToolFactoryMacro.h" #include "mitkMessage.h" #include "mitkDataNode.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateNot.h" #include #include #include #include "mitkInteractionEventObserver.h" #include "mitkEventStateMachine.h" +#include namespace us { class ModuleResource; } namespace mitk { class ToolManager; /** \brief Base class of all tools used by mitk::ToolManager. \sa ToolManager \sa SegTool2D \ingroup Interaction \ingroup ToolManagerEtAl There is a separate page describing the \ref QmitkInteractiveSegmentationTechnicalPage. Every tool is a mitk::StateMachine, which can follow any transition pattern that it likes. One important thing to know is, that every derived tool should always call SuperClass::Deactivated() in its own implementation of Deactivated, because mitk::Tool resets the StateMachine in this method. Only if you are very sure that you covered all possible things that might happen to your own tool, you should consider not to reset the StateMachine from time to time. To learn about the MITK implementation of state machines in general, have a look at \ref InteractionPage. To derive a non-abstract tool, you inherit from mitk::Tool (or some other base class further down the inheritance tree), and in your own parameterless constructor (that is called from the itkFactorylessNewMacro that you use) you pass a StateMachine pattern name to the superclass. Names for valid patterns can be found in StateMachine.xml (which might be enhanced by you). You have to implement at least GetXPM() and GetName() to provide some identification. Each Tool knows its ToolManager, which can provide the data that the tool should work on. \warning Only to be instantiated by mitk::ToolManager (because SetToolManager has to be called). All other uses are unsupported. $Author$ */ class MITKSEGMENTATION_EXPORT Tool : public EventStateMachine , public InteractionEventObserver { public: - typedef unsigned char DefaultSegmentationDataType; + typedef mitk::Label::PixelType DefaultSegmentationDataType; /** * \brief To let GUI process new events (e.g. qApp->processEvents() ) */ Message<> GUIProcessEventsMessage; /** * \brief To send error messages (to be shown by some GUI) */ Message1 ErrorMessage; /** * \brief To send whether the tool is busy (to be shown by some GUI) */ Message1 CurrentlyBusy; /** * \brief To send general messages (to be shown by some GUI) */ Message1 GeneralMessage; mitkClassMacro(Tool, EventStateMachine); // no New(), there should only be subclasses /** \brief Returns an icon in the XPM format. This icon has to fit into some kind of button in most applications, so make it smaller than 25x25 pixels. XPM is e.g. supported by The Gimp. But if you open any XPM file in your text editor, you will see that you could also "draw" it with an editor. */ virtual const char** GetXPM() const = 0; /** * \brief Returns the path of an icon. * * This icon is preferred to the XPM icon. */ virtual std::string GetIconPath() const { return ""; } /** * \brief Returns the path of a cursor icon. * */ virtual us::ModuleResource GetCursorIconResource() const; /** * @brief Returns the tool button icon of the tool wrapped by a usModuleResource * @return a valid ModuleResource or an invalid if this function * is not reimplemented */ virtual us::ModuleResource GetIconResource() const; /** \brief Returns the name of this tool. Make it short! This name has to fit into some kind of button in most applications, so take some time to think of a good name! */ virtual const char* GetName() const = 0; /** \brief Name of a group. You can group several tools by assigning a group name. Graphical tool selectors might use this information to group tools. (What other reason could there be?) */ virtual const char* GetGroup() const; virtual void InitializeStateMachine(); /** * \brief Interface for GUI creation. * * This is the basic interface for creation of a GUI object belonging to one tool. * * Tools that support a GUI (e.g. for display/editing of parameters) should follow some rules: * * - A Tool and its GUI are two separate classes * - There may be several instances of a GUI at the same time. * - mitk::Tool is toolkit (Qt, wxWidgets, etc.) independent, the GUI part is of course dependent * - The GUI part inherits both from itk::Object and some GUI toolkit class * - The GUI class name HAS to be constructed like "toolkitPrefix" tool->GetClassName() + "toolkitPostfix", e.g. MyTool -> wxMyToolGUI * - For each supported toolkit there is a base class for tool GUIs, which contains some convenience methods * - Tools notify the GUI about changes using ITK events. The GUI must observe interesting events. * - The GUI base class may convert all ITK events to the GUI toolkit's favoured messaging system (Qt -> signals) * - Calling methods of a tool by its GUI is done directly. * In some cases GUIs don't want to be notified by the tool when they cause a change in a tool. * There is a macro CALL_WITHOUT_NOTICE(method()), which will temporarily disable all notifications during a method call. */ virtual itk::Object::Pointer GetGUI(const std::string& toolkitPrefix, const std::string& toolkitPostfix); virtual NodePredicateBase::ConstPointer GetReferenceDataPreference() const; virtual NodePredicateBase::ConstPointer GetWorkingDataPreference() const; DataNode::Pointer CreateEmptySegmentationNode( Image* original, const std::string& organName, const mitk::Color& color ); DataNode::Pointer CreateSegmentationNode( Image* image, const std::string& organName, const mitk::Color& color ); virtual bool CanHandle(BaseData* referenceData) const; protected: friend class ToolManager; virtual void SetToolManager(ToolManager*); void ConnectActionsAndFunctions() override; /** \brief Called when the tool gets activated (registered to mitk::GlobalInteraction). Derived tools should call their parents implementation. */ virtual void Activated(); /** \brief Called when the tool gets deactivated (unregistered from mitk::GlobalInteraction). Derived tools should call their parents implementation. */ virtual void Deactivated(); Tool(); // purposely hidden Tool( const char*); // purposely hidden virtual ~Tool(); virtual void Notify( InteractionEvent* interactionEvent,bool isHandled ) override; bool FilterEvents(InteractionEvent* , DataNode* ) override; ToolManager* m_ToolManager; private: // for reference data NodePredicateDataType::Pointer m_PredicateImages; NodePredicateDimension::Pointer m_PredicateDim3; NodePredicateDimension::Pointer m_PredicateDim4; NodePredicateOr::Pointer m_PredicateDimension; NodePredicateAnd::Pointer m_PredicateImage3D; NodePredicateProperty::Pointer m_PredicateBinary; NodePredicateNot::Pointer m_PredicateNotBinary; NodePredicateProperty::Pointer m_PredicateSegmentation; NodePredicateNot::Pointer m_PredicateNotSegmentation; NodePredicateProperty::Pointer m_PredicateHelper; NodePredicateNot::Pointer m_PredicateNotHelper; NodePredicateAnd::Pointer m_PredicateImageColorful; NodePredicateAnd::Pointer m_PredicateImageColorfulNotHelper; NodePredicateAnd::Pointer m_PredicateReference; // for working data NodePredicateAnd::Pointer m_IsSegmentationPredicate; std::string m_InteractorType; }; } // namespace #endif diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp index 96c3591165..14dd616c44 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp @@ -1,183 +1,192 @@ /*=================================================================== 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 "mitkBooleanOperation.h" #include #include #include #include #include #include -typedef itk::Image ImageType; +typedef itk::Image< mitk::Label::PixelType, 3> ImageType; static mitk::Image::Pointer Get3DSegmentation(mitk::Image::Pointer segmentation, unsigned int time) { if (segmentation->GetDimension() != 4) return segmentation; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(segmentation); imageTimeSelector->SetTimeNr(static_cast(time)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } static ImageType::Pointer CastTo3DItkImage(mitk::Image::Pointer segmentation, unsigned int time) { ImageType::Pointer result; mitk::CastToItkImage(Get3DSegmentation(segmentation, time), result); return result; } mitk::BooleanOperation::BooleanOperation(Type type, mitk::Image::Pointer segmentation0, mitk::Image::Pointer segmentation1, unsigned int time) : m_Type(type), m_Segmentation0(segmentation0), m_Segmentation1(segmentation1), m_Time(time) { this->ValidateSegmentations(); } mitk::BooleanOperation::~BooleanOperation() { } -mitk::Image::Pointer mitk::BooleanOperation::GetResult() const +mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetResult() const { - Image::Pointer result; + mitk::LabelSetImage::Pointer result; switch (m_Type) { case Difference: result = this->GetDifference(); break; case Intersection: result = this->GetIntersection(); break; case Union: result = this->GetUnion(); break; default: mitkThrow() << "Unknown boolean operation type '" << m_Type << "'!"; } return result; } -mitk::Image::Pointer mitk::BooleanOperation::GetDifference() const +mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetDifference() const { ImageType::Pointer input1 = CastTo3DItkImage(m_Segmentation0, m_Time); ImageType::Pointer input2 = CastTo3DItkImage(m_Segmentation1, m_Time); itk::NotImageFilter::Pointer notFilter = itk::NotImageFilter::New(); notFilter->SetInput(input2); itk::AndImageFilter::Pointer andFilter = itk::AndImageFilter::New(); andFilter->SetInput1(input1); andFilter->SetInput2(notFilter->GetOutput()); andFilter->UpdateLargestPossibleRegion(); - Image::Pointer result = Image::New(); - CastToMitkImage(andFilter->GetOutput(), result); + Image::Pointer tempResult = Image::New(); + CastToMitkImage(andFilter->GetOutput(), tempResult); - result->DisconnectPipeline(); + tempResult->DisconnectPipeline(); + + mitk::LabelSetImage::Pointer result = mitk::LabelSetImage::New(); + result->InitializeByLabeledImage(tempResult ); return result; } -mitk::Image::Pointer mitk::BooleanOperation::GetIntersection() const +mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetIntersection() const { ImageType::Pointer input1 = CastTo3DItkImage(m_Segmentation0, m_Time); ImageType::Pointer input2 = CastTo3DItkImage(m_Segmentation1, m_Time); itk::AndImageFilter::Pointer andFilter = itk::AndImageFilter::New(); andFilter->SetInput1(input1); andFilter->SetInput2(input2); andFilter->UpdateLargestPossibleRegion(); - Image::Pointer result = Image::New(); - CastToMitkImage(andFilter->GetOutput(), result); + Image::Pointer tempResult = Image::New(); + CastToMitkImage(andFilter->GetOutput(), tempResult); - result->DisconnectPipeline(); + tempResult->DisconnectPipeline(); + mitk::LabelSetImage::Pointer result = mitk::LabelSetImage::New(); + result->InitializeByLabeledImage(tempResult); return result; } -mitk::Image::Pointer mitk::BooleanOperation::GetUnion() const +mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetUnion() const { ImageType::Pointer input1 = CastTo3DItkImage(m_Segmentation0, m_Time); ImageType::Pointer input2 = CastTo3DItkImage(m_Segmentation1, m_Time); itk::OrImageFilter::Pointer orFilter = itk::OrImageFilter::New(); orFilter->SetInput1(input1); orFilter->SetInput2(input2); orFilter->UpdateLargestPossibleRegion(); - Image::Pointer result = Image::New(); - CastToMitkImage(orFilter->GetOutput(), result); + Image::Pointer tempResult = Image::New(); + CastToMitkImage(orFilter->GetOutput(), tempResult); - result->DisconnectPipeline(); + tempResult->DisconnectPipeline(); + mitk::LabelSetImage::Pointer result = mitk::LabelSetImage::New(); + result->InitializeByLabeledImage(tempResult); return result; } void mitk::BooleanOperation::ValidateSegmentation(mitk::Image::Pointer segmentation) const { if (segmentation.IsNull()) mitkThrow() << "Segmentation is NULL!"; if (segmentation->GetImageDescriptor()->GetNumberOfChannels() != 1) mitkThrow() << "Segmentation has more than one channel!"; mitk::PixelType pixelType = segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType(); - if (pixelType.GetPixelType() != itk::ImageIOBase::SCALAR || pixelType.GetComponentType() != itk::ImageIOBase::UCHAR) - mitkThrow() << "Segmentation is not a scalar image of type 'unsigned char'!"; + if (pixelType.GetPixelType() != itk::ImageIOBase::SCALAR || ( pixelType.GetComponentType() != itk::ImageIOBase::UCHAR && pixelType.GetComponentType() != itk::ImageIOBase::USHORT ) ) + { + mitkThrow() << "Segmentation is not of a supported type. Supported are scalar images of type 'unsigned char' or 'unsigned short'."; + } unsigned int dimension = segmentation->GetDimension(); if (dimension > 4) mitkThrow() << "Segmentation has more than four dimensions!"; if (m_Time != 0) { if(dimension < 4) mitkThrow() << "Expected four-dimensional segmentation!"; if (segmentation->GetDimension(3) < m_Time) mitkThrow() << "Extend of fourth dimension of segmentation is less than specified time!"; } else if (dimension < 3) { mitkThrow() << "Segmentation has less than three dimensions!"; } } void mitk::BooleanOperation::ValidateSegmentations() const { this->ValidateSegmentation(m_Segmentation0); this->ValidateSegmentation(m_Segmentation1); if (m_Segmentation0->GetDimension() != m_Segmentation1->GetDimension()) mitkThrow() << "Segmentations have different dimensions!"; } diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h index fb378bd833..f4e73569f9 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h @@ -1,77 +1,77 @@ /*=================================================================== 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 mitkBooleanOperation_h #define mitkBooleanOperation_h -#include +#include #include namespace mitk { /** \brief Executes a boolean operation on two different segmentations. * * All parameters of the boolean operations must be specified during construction. * The actual operation is executed when calling GetResult(). */ class MITKSEGMENTATION_EXPORT BooleanOperation { public: enum Type { None, Difference, Intersection, Union }; /* \brief Construct a boolean operation. * * Throws an mitk::Exception when segmentations are somehow invalid. * * \param[in] type The type of the boolean operation. * \param[in] segmentation1 The first operand of the boolean operation. * \param[in] segmentation2 The second operand of the boolean operation. * \param[in] The time step at which the operation will be executed. */ BooleanOperation(Type type, Image::Pointer segmentation1, Image::Pointer segmentation2, unsigned int time = 0); ~BooleanOperation(); /* \brief Execute boolean operation and return resulting segmentation. * * \return The resulting segmentation. */ - Image::Pointer GetResult() const; + LabelSetImage::Pointer GetResult() const; private: BooleanOperation(const BooleanOperation &); BooleanOperation & operator=(const BooleanOperation &); - Image::Pointer GetDifference() const; - Image::Pointer GetIntersection() const; - Image::Pointer GetUnion() const; + LabelSetImage::Pointer GetDifference() const; + LabelSetImage::Pointer GetIntersection() const; + LabelSetImage::Pointer GetUnion() const; void ValidateSegmentation(Image::Pointer segmentation) const; void ValidateSegmentations() const; Type m_Type; Image::Pointer m_Segmentation0; Image::Pointer m_Segmentation1; unsigned int m_Time; }; } #endif diff --git a/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp b/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp index 002544b05c..11c2402ede 100644 --- a/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp +++ b/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp @@ -1,359 +1,359 @@ /*=================================================================== 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 "mitkMorphologicalOperations.h" #include #include #include #include #include #include #include #include #include #include #include void mitk::MorphologicalOperations::Closing(mitk::Image::Pointer& image, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElement) { MITK_INFO << "Start Closing..."; int timeSteps = static_cast(image->GetTimeSteps()); if (timeSteps > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); for (int t = 0; t < timeSteps; ++t) { MITK_INFO << " Processing time step " << t; timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer img3D = timeSelector->GetOutput(); img3D->DisconnectPipeline(); - AccessFixedPixelTypeByItk_3(img3D, itkClosing, (unsigned char), img3D, factor, structuralElement); + AccessByItk_3(img3D, itkClosing, img3D, factor, structuralElement); mitk::ImageReadAccessor accessor(img3D); image->SetVolume(accessor.GetData(), t); } } else { - AccessFixedPixelTypeByItk_3(image, itkClosing, (unsigned char), image, factor, structuralElement); + AccessByItk_3(image, itkClosing, image, factor, structuralElement); } MITK_INFO << "Finished Closing"; } void mitk::MorphologicalOperations::Erode(mitk::Image::Pointer& image, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElement) { MITK_INFO << "Start Erode..."; int timeSteps = static_cast(image->GetTimeSteps()); if (timeSteps > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); for (int t = 0; t < timeSteps; ++t) { MITK_INFO << " Processing time step " << t; timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer img3D = timeSelector->GetOutput(); img3D->DisconnectPipeline(); - AccessFixedPixelTypeByItk_3(img3D, itkErode, (unsigned char), img3D, factor, structuralElement); + AccessByItk_3(img3D, itkErode, img3D, factor, structuralElement); mitk::ImageReadAccessor accessor(img3D); image->SetVolume(accessor.GetData(), t); } } else { - AccessFixedPixelTypeByItk_3(image, itkErode, (unsigned char), image, factor, structuralElement); + AccessByItk_3(image, itkErode, image, factor, structuralElement); } MITK_INFO << "Finished Erode"; } void mitk::MorphologicalOperations::Dilate(mitk::Image::Pointer& image, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElement) { MITK_INFO << "Start Dilate..."; int timeSteps = static_cast(image->GetTimeSteps()); if (timeSteps > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); for (int t = 0; t < timeSteps; ++t) { MITK_INFO << " Processing time step " << t; timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer img3D = timeSelector->GetOutput(); img3D->DisconnectPipeline(); - AccessFixedPixelTypeByItk_3(img3D, itkDilate, (unsigned char), img3D, factor, structuralElement); + AccessByItk_3(img3D, itkDilate, img3D, factor, structuralElement); mitk::ImageReadAccessor accessor(img3D); image->SetVolume(accessor.GetData(), t); } } else { - AccessFixedPixelTypeByItk_3(image, itkDilate, (unsigned char), image, factor, structuralElement); + AccessByItk_3(image, itkDilate, image, factor, structuralElement); } MITK_INFO << "Finished Dilate"; } void mitk::MorphologicalOperations::Opening(mitk::Image::Pointer& image, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElement) { MITK_INFO << "Start Opening..."; int timeSteps = static_cast(image->GetTimeSteps()); if (timeSteps > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); for (int t = 0; t < timeSteps; ++t) { MITK_INFO << " Processing time step " << t; timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer img3D = timeSelector->GetOutput(); img3D->DisconnectPipeline(); - AccessFixedPixelTypeByItk_3(img3D, itkOpening, (unsigned char), img3D, factor, structuralElement); + AccessByItk_3(img3D, itkOpening, img3D, factor, structuralElement); mitk::ImageReadAccessor accessor(img3D); image->SetVolume(accessor.GetData(), t); } } else { - AccessFixedPixelTypeByItk_3(image, itkOpening, (unsigned char), image, factor, structuralElement); + AccessByItk_3(image, itkOpening, image, factor, structuralElement); } MITK_INFO << "Finished Opening"; } void mitk::MorphologicalOperations::FillHoles(mitk::Image::Pointer &image) { MITK_INFO << "Start FillHole..."; int timeSteps = static_cast(image->GetTimeSteps()); if (timeSteps > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); for (int t = 0; t < timeSteps; ++t) { MITK_INFO << " Processing time step " << t; timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer img3D = timeSelector->GetOutput(); img3D->DisconnectPipeline(); - AccessFixedPixelTypeByItk_1(img3D, itkFillHoles, (unsigned char), img3D); + AccessByItk_1(img3D, itkFillHoles, img3D); mitk::ImageReadAccessor accessor(img3D); image->SetVolume(accessor.GetData(), t); } } else { - AccessFixedPixelTypeByItk_1(image, itkFillHoles, (unsigned char), image); + AccessByItk_1(image, itkFillHoles, image); } MITK_INFO << "Finished FillHole"; } template void mitk::MorphologicalOperations::itkClosing(itk::Image* sourceImage, mitk::Image::Pointer& resultImage, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElementFlags) { typedef itk::Image ImageType; typedef itk::BinaryBallStructuringElement BallType; typedef itk::BinaryCrossStructuringElement CrossType; typedef typename itk::BinaryMorphologicalClosingImageFilter BallClosingFilterType; typedef typename itk::BinaryMorphologicalClosingImageFilter CrossClosingFilterType; if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagital)) { BallType ball = CreateStructuringElement(structuralElementFlags,factor); typename BallClosingFilterType::Pointer closingFilter = BallClosingFilterType::New(); closingFilter->SetKernel(ball); closingFilter->SetInput(sourceImage); closingFilter->SetForegroundValue(1); closingFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(closingFilter->GetOutput(), resultImage); } else { CrossType cross = CreateStructuringElement(structuralElementFlags,factor); typename CrossClosingFilterType::Pointer closingFilter = CrossClosingFilterType::New(); closingFilter->SetKernel(cross); closingFilter->SetInput(sourceImage); closingFilter->SetForegroundValue(1); closingFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(closingFilter->GetOutput(), resultImage); } } template void mitk::MorphologicalOperations::itkErode(itk::Image* sourceImage, mitk::Image::Pointer& resultImage, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElementFlags) { typedef itk::Image ImageType; typedef itk::BinaryBallStructuringElement BallType; typedef itk::BinaryCrossStructuringElement CrossType; typedef typename itk::BinaryErodeImageFilter BallErodeFilterType; typedef typename itk::BinaryErodeImageFilter CrossErodeFilterType; if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagital)) { BallType ball = CreateStructuringElement(structuralElementFlags,factor); typename BallErodeFilterType::Pointer erodeFilter = BallErodeFilterType::New(); erodeFilter->SetKernel(ball); erodeFilter->SetInput(sourceImage); erodeFilter->SetErodeValue(1); erodeFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } else { CrossType cross = CreateStructuringElement(structuralElementFlags,factor); typename CrossErodeFilterType::Pointer erodeFilter = CrossErodeFilterType::New(); erodeFilter->SetKernel(cross); erodeFilter->SetInput(sourceImage); erodeFilter->SetErodeValue(1); erodeFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage); } } template void mitk::MorphologicalOperations::itkDilate(itk::Image* sourceImage, mitk::Image::Pointer& resultImage, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElementFlags) { typedef itk::Image ImageType; typedef itk::BinaryBallStructuringElement BallType; typedef itk::BinaryCrossStructuringElement CrossType; typedef typename itk::BinaryDilateImageFilter BallDilateFilterType; typedef typename itk::BinaryDilateImageFilter CrossDilateFilterType; if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagital)) { BallType ball = CreateStructuringElement(structuralElementFlags,factor); typename BallDilateFilterType::Pointer dilateFilter = BallDilateFilterType::New(); dilateFilter->SetKernel(ball); dilateFilter->SetInput(sourceImage); dilateFilter->SetDilateValue(1); dilateFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(dilateFilter->GetOutput(), resultImage); } else { CrossType cross = CreateStructuringElement(structuralElementFlags,factor); typename CrossDilateFilterType::Pointer dilateFilter = CrossDilateFilterType::New(); dilateFilter->SetKernel(cross); dilateFilter->SetInput(sourceImage); dilateFilter->SetDilateValue(1); dilateFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(dilateFilter->GetOutput(), resultImage); } } template void mitk::MorphologicalOperations::itkOpening(itk::Image* sourceImage, mitk::Image::Pointer& resultImage, int factor, mitk::MorphologicalOperations::StructuralElementType structuralElementFlags) { typedef itk::Image ImageType; typedef itk::BinaryBallStructuringElement BallType; typedef itk::BinaryCrossStructuringElement CrossType; typedef typename itk::BinaryMorphologicalOpeningImageFilter BallOpeningFiltertype; typedef typename itk::BinaryMorphologicalOpeningImageFilter CrossOpeningFiltertype; if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagital)) { BallType ball = CreateStructuringElement(structuralElementFlags,factor); typename BallOpeningFiltertype::Pointer openingFilter = BallOpeningFiltertype::New(); openingFilter->SetKernel(ball); openingFilter->SetInput(sourceImage); openingFilter->SetForegroundValue(1); openingFilter->SetBackgroundValue(0); openingFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(openingFilter->GetOutput(), resultImage); } else { CrossType cross = CreateStructuringElement(structuralElementFlags,factor); typename CrossOpeningFiltertype::Pointer openingFilter = CrossOpeningFiltertype::New(); openingFilter->SetKernel(cross); openingFilter->SetInput(sourceImage); openingFilter->SetForegroundValue(1); openingFilter->SetBackgroundValue(0); openingFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(openingFilter->GetOutput(), resultImage); } } template void mitk::MorphologicalOperations::itkFillHoles(itk::Image* sourceImage, mitk::Image::Pointer& resultImage) { typedef itk::Image ImageType; typedef typename itk::BinaryFillholeImageFilter FillHoleFilterType; typename FillHoleFilterType::Pointer fillHoleFilter = FillHoleFilterType::New(); fillHoleFilter->SetInput(sourceImage); fillHoleFilter->SetForegroundValue(1); fillHoleFilter->UpdateLargestPossibleRegion(); mitk::CastToMitkImage(fillHoleFilter->GetOutput(), resultImage); } diff --git a/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp b/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp index 49e654824a..89738e4c0b 100644 --- a/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp +++ b/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp @@ -1,219 +1,220 @@ /*=================================================================== 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 "mitkTestingMacros.h" -#include -#include -#include +#include +#include #include +#include +#include #include +#include +#include +#include "mitkTestingMacros.h" #include -#include -#include #include class mitkToolInteractionTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkToolInteractionTestSuite); /// \todo Fix VTK memory leaks. Bug 18098. vtkDebugLeaks::SetExitError(0); /* ####### example ###### MITK_TEST(AddToolInteraction_4D_Test); #########################*/ /// \todo fix crash when wipe tool test is called after another test MITK_TEST(WipeToolInteractionTest); MITK_TEST(AddToolInteractionTest); MITK_TEST(SubtractToolInteractionTest); MITK_TEST(PaintToolInteractionTest); MITK_TEST(RegionGrowingToolInteractionTest); MITK_TEST(CorrectionToolInteractionTest); MITK_TEST(EraseToolInteractionTest); MITK_TEST(FillToolInteractionTest); CPPUNIT_TEST_SUITE_END(); private: mitk::DataStorage::Pointer m_DataStorage; mitk::ToolManager::Pointer m_ToolManager; public: int GetToolIdByToolName (const std::string& toolName) { //find tool from toolname int numberOfTools = m_ToolManager->GetTools().size(); int toolId = 0; for(; toolId < numberOfTools; ++toolId) { mitk::Tool* currentTool = m_ToolManager->GetToolById(toolId); if(toolName.compare( currentTool->GetNameOfClass() ) == 0) { return toolId; } } return -1; } void RunTestWithParameters(const std::string& patientImagePath, const std::string& referenceSegmentationImage, const std::string& toolName, const std::string& interactionPattern, unsigned int timestep=0, const std::string& preSegmentationImagePath = std::string()) { //Create test helper to initialize all necessary objects for interaction mitk::InteractionTestHelper interactionTestHelper(GetTestDataFilePath(interactionPattern)); //Use data storage of test helper m_DataStorage = interactionTestHelper.GetDataStorage().GetPointer(); //create ToolManager m_ToolManager = mitk::ToolManager::New(m_DataStorage); m_ToolManager->InitializeTools(); m_ToolManager->RegisterClient();//This is needed because there must be at least one registered. Otherwise tools can't be activated. //Load patient image mitk::Image::Pointer patientImage = mitk::IOUtil::LoadImage(GetTestDataFilePath(patientImagePath)); CPPUNIT_ASSERT(patientImage.IsNotNull()); mitk::DataNode::Pointer patientImageNode = mitk::DataNode::New(); patientImageNode->SetData(patientImage); //Activate tool to work with int toolID = GetToolIdByToolName(toolName); mitk::Tool* tool = m_ToolManager->GetToolById(toolID); CPPUNIT_ASSERT(tool != NULL); //Create empty segmentation working image mitk::DataNode::Pointer workingImageNode = mitk::DataNode::New(); const std::string organName = "test"; mitk::Color color;//actually it dosn't matter which color we are using color.SetRed(1); //but CreateEmptySegmentationNode expects a color parameter color.SetGreen(0); color.SetBlue(0); if(preSegmentationImagePath.empty()) { workingImageNode = tool->CreateEmptySegmentationNode(patientImage, organName, color); } else { mitk::Image::Pointer preSegmentation = mitk::IOUtil::LoadImage(GetTestDataFilePath(preSegmentationImagePath)); workingImageNode = tool->CreateSegmentationNode(preSegmentation, organName, color); } CPPUNIT_ASSERT(workingImageNode.IsNotNull()); CPPUNIT_ASSERT(workingImageNode->GetData() != NULL); //add images to datastorage interactionTestHelper.AddNodeToStorage(patientImageNode); interactionTestHelper.AddNodeToStorage(workingImageNode); //set reference and working image m_ToolManager->SetWorkingData(workingImageNode); m_ToolManager->SetReferenceData(patientImageNode); //set time step interactionTestHelper.SetTimeStep(timestep); //load interaction events m_ToolManager->ActivateTool(toolID); CPPUNIT_ASSERT(m_ToolManager->GetActiveTool() != NULL); //Start Interaction interactionTestHelper.PlaybackInteraction(); //load reference segmentation image mitk::Image::Pointer segmentationReferenceImage = mitk::IOUtil::LoadImage(GetTestDataFilePath(referenceSegmentationImage)); mitk::Image::Pointer currentSegmentationImage = mitk::Image::New(); currentSegmentationImage = dynamic_cast(workingImageNode->GetData()); //compare reference with interaction result MITK_ASSERT_EQUAL(segmentationReferenceImage, currentSegmentationImage, "Reference equals interaction result." ); } void setUp() { } void tearDown() { m_ToolManager->ActivateTool(-1); m_ToolManager = NULL; } void AddToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_AddTool.nrrd", "AddContourTool", "InteractionTestData/Interactions/SegmentationInteractor_AddTool.xml"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_AddContourTool.nrrd", "AddContourTool", "InteractionTestData/Interactions/SegmentationInteractor_AddTool.xml"); } void SubtractToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_SubtractTool.nrrd", "SubtractContourTool", "InteractionTestData/Interactions/SegmentationInteractor_SubtractTool.xml"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_SubtractContourTool.nrrd", "SubtractContourTool", "InteractionTestData/Interactions/SegmentationInteractor_SubtractTool.xml"); } void PaintToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_PaintTool.nrrd", "DrawPaintbrushTool", "InteractionTestData/Interactions/SegmentationInteractor_PaintTool.xml"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_DrawPaintbrushTool.nrrd", "DrawPaintbrushTool", "InteractionTestData/Interactions/SegmentationInteractor_PaintTool.xml"); } void WipeToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_WipeTool.nrrd", "ErasePaintbrushTool", "InteractionTestData/Interactions/SegmentationInteractor_WipeTool.xml"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_ErasePaintbrushTool.nrrd", "ErasePaintbrushTool", "InteractionTestData/Interactions/SegmentationInteractor_WipeTool.xml"); } void RegionGrowingToolInteractionTest() { RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_RegionGrowingTool.nrrd", "RegionGrowingTool", "InteractionTestData/Interactions/SegmentationInteractor_RegionGrowingTool.xml"); } void CorrectionToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_CorrectionTool.nrrd", "CorrectorTool2D", "InteractionTestData/Interactions/SegmentationInteractor_CorrectionTool.xml"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_CorrectorTool2D.nrrd", "CorrectorTool2D", "InteractionTestData/Interactions/SegmentationInteractor_CorrectionTool.xml"); } void EraseToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_EraseTool.nrrd", "EraseRegionTool", "InteractionTestData/Interactions/SegmentationInteractor_EraseTool.xml", 0, "InteractionTestData/ReferenceData/SegmentationInteractor_AddTool.nrrd"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_EraseRegionTool.nrrd", "EraseRegionTool", "InteractionTestData/Interactions/SegmentationInteractor_EraseTool.xml", 0, "InteractionTestData/ReferenceData/SegmentationInteractor_AddTool.nrrd"); } void FillToolInteractionTest() { - RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_FillTool.nrrd", "FillRegionTool", "InteractionTestData/Interactions/SegmentationInteractor_FillTool.xml", 0, "InteractionTestData/InputData/SegmentationInteractor_FillTool_input.nrrd"); + RunTestWithParameters("Pic3D.nrrd", "InteractionTestData/ReferenceData/SegmentationInteractor_FillRegionTool.nrrd", "FillRegionTool", "InteractionTestData/Interactions/SegmentationInteractor_FillTool.xml", 0, "InteractionTestData/InputData/SegmentationInteractor_FillTool_input.nrrd"); } /*############ example ################### void AddToolInteraction_4D_Test() { RunTestWithParameters("US4DCyl.nrrd", "Segmentation/ReferenceSegmentations/AddTool_4D.nrrd", "AddContourTool", "Segmentation/InteractionPatterns/AddTool_4D.xml", 1); } #########################################*/ }; MITK_TEST_SUITE_REGISTRATION(mitkToolInteraction) diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index e989ca53fb..4279c43fa3 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,861 +1,861 @@ /*=================================================================== 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 "QmitkAdaptiveRegionGrowingToolGUI.h" #include "QmitkStdMultiWidget.h" #include #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageTimeSelector.h" #include "mitkImageStatisticsHolder.h" #include #include #include #include #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "QmitkConfirmSegmentationDialog.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImagePixelReadAccessor.h" MITK_TOOL_GUI_MACRO( , QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget* parent) : QmitkToolGUI(), m_MultiWidget(nullptr), m_DataStorage(nullptr), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); //Not yet available //m_Controls.m_PreviewSlider->InvertedAppearance(true); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation","RGResult","RGFeedbackSurface"); connect( this, SIGNAL(NewToolAssociated(mitk::Tool*)), this, SLOT(OnNewToolAssociated(mitk::Tool*)) ); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { //Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetMoveObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool* tool) { m_RegionGrow3DTool = dynamic_cast (tool); if(m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode( this->m_RegionGrow3DTool->GetReferenceData() ); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); //Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver( mitk::PointSetAddEvent(), pointAddedCommand); m_PointSetMoveObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver( mitk::PointSetMoveEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if( imageNode.IsNotNull() ) { m_DataStorage->Remove(imageNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { //Connecting GUI components connect( (QObject*) (m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect( m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect( (QObject*) (m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( (QObject*) (m_Controls.m_cbVolumeRendering), SIGNAL(toggled(bool)), this, SLOT(UseVolumeRendering(bool) )); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { m_MultiWidget = multiWidget; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode* node) { m_InputImageNode = node; mitk::Image* inputImage = dynamic_cast(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image::Pointer im, mitk::Point3D p, int & val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode* node = m_RegionGrow3DTool->GetPointSetNode(); if (node != NULL) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(NULL, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); mitk::Image* image = dynamic_cast(m_InputImageNode->GetData()); mitk::Point3D seedPoint = pointSet->GetPointSet(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep())->GetPoints()->ElementAt(0); if (image->GetGeometry()->IsInside(seedPoint)) mitkPixelTypeMultiplex3(AccessPixel,image->GetChannelDescriptor().GetPixelType(),image,seedPoint,m_SeedpointValue) else return; /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; itk::Index<3> currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos (0); image->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for(int i = runningIndex[0]-2; i <= runningIndex[0]+2; i++) { for(int j = runningIndex[1]-2; j <= runningIndex[1]+2; j++) { for(int k = runningIndex[2]-2; k <= runningIndex[2]+2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if(image->GetGeometry()->IsIndexInside(currentIndex)) { int component = 0; m_InputImageNode->GetIntProperty("Image.Displayed Component", component); mitkPixelTypeMultiplex4(mitk::FastSinglePixelAccess,image->GetChannelDescriptor().GetPixelType(),image,NULL,currentIndex,pixelValues[pos]); pos++; } else { pixelValues[pos] = -10000000; pos++; } } } } //Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (auto & pixelValue : pixelValues) { if(pixelValue > -10000000) { m_SeedPointValueMean += pixelValue; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean/numberOfValues; /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is meanSeedValue+0.15*windowsSize */ mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType windowSize = max - min; windowSize = 0.15*windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue < m_SeedPointValueMean) m_LOWERTHRESHOLD = m_SeedpointValue; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please insert a seed point inside the image.\n\nFirst press the \"Define Seed Point\" button,\nthen click left mouse button inside the image."); return; } //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* timedImage = timeSelector->GetOutput(); AccessByItk_2( timedImage , StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { //QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); //QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor();//reset cursor QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor();//reset cursor } template void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::MinimumMaximumImageCalculator MinMaxValueFilterType; if ( !imageGeometry->IsInside(seedPoint) ) { QApplication::restoreOverrideCursor();//reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( NULL, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices if (m_SeedpointValue>m_UPPERTHRESHOLD || m_SeedpointValueSetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); //In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. //Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter regionGrower->SetLower( m_LOWERTHRESHOLD-1 ); regionGrower->SetUpper( m_UPPERTHRESHOLD+1); try { regionGrower->Update(); } catch(itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch( ... ) { QMessageBox::critical( NULL, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); //initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_LOWERTHRESHOLD+resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if(m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean-1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean+1); } this->m_SliderInitialized = true; //create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData( resultImage ); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0,1.0,0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); //delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add( newNode, m_InputImageNode ); this->InitializeLevelWindow(); if(m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true;// reset first stored threshold value //Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { //get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); mitk::ScalarType* level = new mitk::ScalarType(0.0); mitk::ScalarType* window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue) - m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; //inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint-1; if(m_MultiWidget) { this->m_MultiWidget->levelWindowWidget->GetManager()->SetAutoTopMostImage(false); this->m_MultiWidget->levelWindowWidget->GetManager()->SetLevelWindowProperty(static_cast(newNode->GetProperty("levelwindow"))); } if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (*level + 0.5));//lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { //do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //get the levelWindow associated with the preview mitk::ScalarType level;// = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType* window = new mitk::ScalarType(1); //adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD +0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (level - 0.5));//lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { //moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { //moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { //get image node if(m_InputImageNode.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } //get image data mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if(orgImage.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Image found!"); return; } //get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image*)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(labeledSeg.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch(result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image* img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); //TODO disable slider etc... } template void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image* itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool-> GetTargetSegmentationNode()->GetData()); int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); if (originalSegmentation) { typedef itk::Image InputImageType; - typedef itk::Image SegmentationType; + typedef itk::Image SegmentationType; //select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if(originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( originalSegmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage( timeSelector->GetOutput(), originalSegmentationInITK ); } else //use original { CastToItkImage( originalSegmentation, originalSegmentationInITK ); } //Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator itOutput( originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion() ); itk::ImageRegionIterator itInput( itkImage, itkImage->GetLargestPossibleRegion() ); itOutput.GoToBegin(); itInput.GoToBegin(); //calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } //iterate over image and set pixel in segmentation according to thresholded labeled image while( !itOutput.IsAtEnd() && !itInput.IsAtEnd() ) { //Use threshold slider to determine if pixel is set to 1 if( itInput.Value() != 0 && itInput.Value() >= currentTreshold ) { itOutput.Set( 1 ); } ++itOutput; ++itInput; } //combine current working segmentation image with our region growing result originalSegmentation->SetVolume( (void*)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled //if even m_DataStorage is NULL leave node NULL mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. //if even m_DataStorage is NULL leave node NULL node = m_DataStorage?m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE):NULL; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(node.IsNull()) return; if(m_MultiWidget) m_MultiWidget->SetWidgetPlanesVisibility(!enable); if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int thValue) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); if (m_UpdateSuggestedThreshold) { m_SuggestedThValue = thValue; m_UpdateSuggestedThreshold = false; } // grayvalue->opacity { vtkPiecewiseFunction *f = tf->GetScalarOpacityFunction(); f->RemoveAllPoints(); f->AddPoint(0, 0); f->AddPoint(thValue+0.5, 0); f->AddPoint(thValue+1.5, 1); f->AddPoint(1000, 1); f->ClampingOn(); f->Modified(); } // grayvalue->color { float a = 255.0; vtkColorTransferFunction *ctf = tf->GetColorTransferFunction(); ctf->RemoveAllPoints(); //ctf->AddRGBPoint(-1000, 0.0, 0.0, 0.0); ctf->AddRGBPoint(m_SuggestedThValue+1, 203/a, 104/a, 102/a); ctf->AddRGBPoint(m_SuggestedThValue, 255/a, 0/a, 0/a); ctf->ClampingOn(); ctf->Modified(); } // GradientOpacityFunction { vtkPiecewiseFunction *gof = tf->GetGradientOpacityFunction(); gof->RemoveAllPoints(); gof->AddPoint(-10000, 1); gof->AddPoint(10000, 1); gof->ClampingOn(); gof->Modified(); } mitk::TransferFunctionProperty::Pointer tfp = mitk::TransferFunctionProperty::New(); tfp->SetValue(tf); node->SetProperty("TransferFunction", tfp); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue( double lowerThreshold ) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue( double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if( node.IsNotNull() ) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp index c2452dc241..f012ac6ed4 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp @@ -1,1270 +1,1273 @@ /*=================================================================== 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 "QmitkSlicesInterpolator.h" #include "QmitkStdMultiWidget.h" #include "QmitkSelectableGLWidget.h" #include "mitkToolManager.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkProgressBar.h" #include "mitkGlobalInteraction.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkSegTool2D.h" #include "mitkCoreObjectFactory.h" #include "mitkSurfaceToImageFilter.h" #include "mitkSliceNavigationController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) float SURFACE_COLOR_RGB [3] = {0.49f, 1.0f, 0.16f}; const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; foreach(mitk::SliceNavigationController* slicer, m_ControllerToDeleteObserverTag.keys()) { actionToSliceDimension[new QAction(QString::fromStdString(slicer->GetViewDirectionAsString()),0)] = slicer; } return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget* parent, const char* /*name*/) :QWidget(parent), // ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator( mitk::SegmentationInterpolationController::New() ), m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()), m_ToolManager(NULL), m_Initialized(false), m_LastSNC(0), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false), m_FirstRun(true) { m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QVBoxLayout* vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode); m_EdgeDetector = mitk::FeatureBasedEdgeDetectionFilter::New(); m_PointScorer = mitk::PointCloudScoringFilter::New(); m_PlaneSuggester = mitk::ClusteredPlaneSuggestionFilter::New(); m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode); m_CmbInterpolation->addItem("Disabled"); m_CmbInterpolation->addItem("2-Dimensional"); m_CmbInterpolation->addItem("3-Dimensional"); vboxLayout->addWidget(m_CmbInterpolation); m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply2D); m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApplyForAllSlices2D); m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply3D); m_BtnSuggestPlane = new QPushButton("Suggest a plane", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnSuggestPlane); m_BtnReinit3DInterpolation = new QPushButton("Reinit Interpolation", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnReinit3DInterpolation); m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_ChkShowPositionNodes); this->HideAllInterpolationControls(); connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int))); connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked())); connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); connect(m_BtnSuggestPlane, SIGNAL(clicked()), this, SLOT(OnSuggestPlaneClicked())); connect(m_BtnReinit3DInterpolation, SIGNAL(clicked()), this, SLOT(OnReinit3DInterpolation())); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged ); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver( itk::ModifiedEvent(), command ); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged ); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver( itk::ModifiedEvent(), command2 ); // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_FeedbackNode ); m_FeedbackNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "color", mitk::ColorProperty::New(255.0, 255.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", mitk::IntProperty::New( 20 ) ); m_FeedbackNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_FeedbackNode->SetProperty( "name", mitk::StringProperty::New("Interpolation feedback") ); m_FeedbackNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_FeedbackNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(SURFACE_COLOR_RGB) ); m_InterpolatedSurfaceNode->SetProperty( "name", mitk::StringProperty::New("Surface Interpolation feedback") ); m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); m_InterpolatedSurfaceNode->SetProperty( "line width", mitk::FloatProperty::New(4.0f) ); m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); m_3DContourNode->SetProperty("hidden object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "name", mitk::StringProperty::New("Drawn Contours") ); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); QWidget::setContentsMargins(0, 0, 0, 0); if ( QWidget::layout() != NULL ) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } //For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage( mitk::DataStorage::Pointer storage ) { m_DataStorage = storage; m_SurfaceInterpolator->SetDataStorage(storage); } mitk::DataStorage* QmitkSlicesInterpolator::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager* toolManager, const QList &controllers) { Q_ASSERT(!controllers.empty()); if (m_Initialized) { // remove old observers Uninitialize(); } m_ToolManager = toolManager; if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode* node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled( node != NULL ); // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); // connect to the slice navigation controller. after each change, call the interpolator foreach(mitk::SliceNavigationController* slicer, controllers) { //Has to be initialized m_LastSNC = slicer; m_TimeStep.insert(slicer, slicer->GetTime()->GetPos()); itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted); m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand)); itk::MemberCommand::Pointer timeChangedCommand = itk::MemberCommand::New(); timeChangedCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTimeChanged); m_ControllerToTimeObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(NULL,0), timeChangedCommand)); itk::MemberCommand::Pointer sliceChangedCommand = itk::MemberCommand::New(); sliceChangedCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSliceChanged); m_ControllerToSliceObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(NULL,0), sliceChangedCommand)); } ACTION_TO_SLICEDIMENSION = createActionToSliceDimension(); } m_Initialized = true; } void QmitkSlicesInterpolator::Uninitialize() { if (m_ToolManager.IsNotNull()) { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified); m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified); } foreach(mitk::SliceNavigationController* slicer, m_ControllerToSliceObserverTag.keys()) { slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer)); } ACTION_TO_SLICEDIMENSION.clear(); m_ToolManager = NULL; m_Initialized = false; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() { if (m_Initialized) { // remove old observers Uninitialize(); } if(m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); // remove observer m_Interpolator->RemoveObserver( InterpolationInfoChangedObserverTag ); m_SurfaceInterpolator->RemoveObserver( SurfaceInterpolationInfoChangedObserverTag ); delete m_Timer; } /** External enableization... */ void QmitkSlicesInterpolator::setEnabled( bool enable ) { QWidget::setEnabled(enable); //Set the gui elements of the different interpolation modi enabled if (enable) { if (m_2DInterpolationEnabled) { this->Show2DInterpolationControls(true); m_Interpolator->Activate2DInterpolation(true); } else if (m_3DInterpolationEnabled) { this->Show3DInterpolationControls(true); this->Show3DInterpolationResult(true); } } //Set all gui elements of the interpolation disabled else { this->HideAllInterpolationControls(); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); m_Interpolator->Activate2DInterpolation(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::HideAllInterpolationControls() { this->Show2DInterpolationControls(false); this->Show3DInterpolationControls(false); } void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show) { m_BtnApply2D->setVisible(show); m_BtnApplyForAllSlices2D->setVisible(show); } void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show) { m_BtnApply3D->setVisible(show); m_BtnSuggestPlane->setVisible(show); m_ChkShowPositionNodes->setVisible(show); m_BtnReinit3DInterpolation->setVisible(show); } void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index) { switch(index) { case 0: // Disabled m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation"); this->HideAllInterpolationControls(); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(false); this->Show3DInterpolationResult(false); m_Interpolator->Activate2DInterpolation(false); break; case 1: // 2D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show2DInterpolationControls(true); this->OnInterpolationActivated(true); this->On3DInterpolationActivated(false); m_Interpolator->Activate2DInterpolation(true); break; case 2: // 3D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show3DInterpolationControls(true); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(true); m_Interpolator->Activate2DInterpolation(false); break; default: MITK_ERROR << "Unknown interpolation method!"; m_CmbInterpolation->setCurrentIndex(0); break; } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_ToolManager->GetWorkingData(0) != 0) { m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); m_BtnReinit3DInterpolation->setEnabled(true); } else { //If no workingdata is set, remove the interpolation feedback this->GetDataStorage()->Remove(m_FeedbackNode); m_FeedbackNode->SetData(NULL); this->GetDataStorage()->Remove(m_3DContourNode); m_3DContourNode->SetData(NULL); this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode); m_InterpolatedSurfaceNode->SetData(NULL); m_BtnReinit3DInterpolation->setEnabled(false); return; } //Updating the current selected segmentation for the 3D interpolation SetCurrentContourListID(); if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } this->CheckSupportedImageDimension(); } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { } void QmitkSlicesInterpolator::OnTimeChanged(itk::Object* sender, const itk::EventObject& e) { //Check if we really have a GeometryTimeEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController* slicer = dynamic_cast(sender); Q_ASSERT(slicer); m_TimeStep[slicer] = slicer->GetTime()->GetPos(); m_SurfaceInterpolator->SetCurrentTimeStep( slicer->GetTime()->GetPos() ); if (m_LastSNC == slicer) { slicer->SendSlice();//will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e) { //Check whether we really have a GeometrySliceEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController* slicer = dynamic_cast(sender); if (TranslateAndInterpolateChangedSlice(e, slicer)) { slicer->GetRenderer()->RequestUpdate(); } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, mitk::SliceNavigationController* slicer) { if (!m_2DInterpolationEnabled) return false; try { const mitk::SliceNavigationController::GeometrySliceEvent& event = dynamic_cast(e); mitk::TimeGeometry* tsg = event.GetTimeGeometry(); if (tsg && m_TimeStep.contains(slicer)) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(tsg->GetGeometryForTimeStep(m_TimeStep[slicer]).GetPointer()); if (slicedGeometry) { m_LastSNC = slicer; mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetPlaneGeometry( event.GetPos() )); if (plane) Interpolate( plane, m_TimeStep[slicer], slicer ); return true; } } } catch(std::bad_cast) { return false; // so what } return false; } void QmitkSlicesInterpolator::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep, mitk::SliceNavigationController* slicer ) { if (m_ToolManager) { mitk::DataNode* node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { int clickedSliceDimension(-1); int clickedSliceIndex(-1); // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex ); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( clickedSliceDimension, clickedSliceIndex, plane, timeStep ); m_FeedbackNode->SetData( interpolation ); m_LastSNC = slicer; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if(interpolatedSurface.IsNotNull() && workingNode && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) { m_BtnApply3D->setEnabled(true); m_BtnSuggestPlane->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); this->Show3DInterpolationResult(true); if( !m_DataStorage->Exists(m_InterpolatedSurfaceNode) ) { m_DataStorage->Add(m_InterpolatedSurfaceNode); } if (!m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode, workingNode); } } else if (interpolatedSurface.IsNull()) { m_BtnApply3D->setEnabled(false); m_BtnSuggestPlane->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { this->Show3DInterpolationResult(false); } } m_BtnReinit3DInterpolation->setEnabled(true); foreach (mitk::SliceNavigationController* slicer, m_ControllerToTimeObserverTag.keys()) { slicer->GetRenderer()->RequestUpdate(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set slice as input mitk::Image::Pointer slice = dynamic_cast(m_FeedbackNode->GetData()); reslice->SetInputSlice(slice->GetSliceData()->GetVtkImageAccessor(slice)->GetVtkImageData()); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( m_Segmentation ); unsigned int timestep = m_LastSNC->GetTime()->GetPos(); extractor->SetTimeStep( timestep ); extractor->SetWorldGeometry( m_LastSNC->GetCurrentPlaneGeometry() ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep( timestep ) ); extractor->Modified(); extractor->Update(); //the image was modified within the pipeline, but not marked so m_Segmentation->Modified(); m_Segmentation->GetVtkImageData()->Modified(); m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController* slicer) { /* * What exactly is done here: * 1. We create an empty diff image for the current segmentation * 2. All interpolated slices are written into the diff image * 3. Then the diffimage is applied to the original segmentation */ if (m_Segmentation) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); mitk::Image::Pointer image3D = m_Segmentation; unsigned int timeStep( slicer->GetTime()->GetPos() ); if (m_Segmentation->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( m_Segmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->Update(); image3D = timeSelector->GetOutput(); } // create a empty diff image for the undo operation mitk::Image::Pointer diffImage = mitk::Image::New(); diffImage->Initialize( image3D ); // Create scope for ImageWriteAccessor so that the accessor is destroyed // after the image is initialized. Otherwise later image access will lead to an error { mitk::ImageWriteAccessor imAccess(diffImage); // Set all pixels to zero mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); memset( imAccess.GetData(), 0, (pixelType.GetBpe() >> 3) * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2) ); } // Since we need to shift the plane it must be clone so that the original plane isn't altered mitk::PlaneGeometry::Pointer reslicePlane = slicer->GetCurrentPlaneGeometry()->Clone(); int sliceDimension(-1); int sliceIndex(-1); mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, reslicePlane, sliceDimension, sliceIndex ); unsigned int zslices = m_Segmentation->GetDimension( sliceDimension ); mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); mitk::Point3D origin = reslicePlane->GetOrigin(); unsigned int totalChangedSlices(0); for (unsigned int sliceIndex = 0; sliceIndex < zslices; ++sliceIndex) { // Transforming the current origin of the reslice plane // so that it matches the one of the next slice m_Segmentation->GetSlicedGeometry()->WorldToIndex(origin, origin); origin[sliceDimension] = sliceIndex; m_Segmentation->GetSlicedGeometry()->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); //Set the slice as 'input' mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( sliceDimension, sliceIndex, reslicePlane, timeStep ); if (interpolation.IsNotNull()) // we don't check if interpolation is necessary/sensible - but m_Interpolator does { //Setting up the reslicing pipeline which allows us to write the interpolation results back into //the image volume vtkSmartPointer reslice = vtkSmartPointer::New(); //set overwrite mode to true to write back to the image volume reslice->SetInputSlice(interpolation->GetSliceData()->GetVtkImageAccessor(interpolation)->GetVtkImageData()); reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer diffslicewriter = mitk::ExtractSliceFilter::New(reslice); diffslicewriter->SetInput( diffImage ); diffslicewriter->SetTimeStep( 0 ); diffslicewriter->SetWorldGeometry(reslicePlane); diffslicewriter->SetVtkOutputRequest(true); diffslicewriter->SetResliceTransformByGeometry( diffImage->GetTimeGeometry()->GetGeometryForTimeStep( 0 ) ); diffslicewriter->Modified(); diffslicewriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); if (totalChangedSlices > 0) { // store undo stack items if ( true ) { // create do/undo operations mitk::ApplyDiffImageOperation* doOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); mitk::ApplyDiffImageOperation* undoOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); undoOp->SetFactor( -1.0 ); std::stringstream comment; comment << "Confirm all interpolations (" << totalChangedSlices << ")"; mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment.str() ); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); // acutally apply the changes here to the original image mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation( doOp ); } } m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController* slicer) { //this redirect is for calling from outside if (slicer == NULL) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations( slicer ); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for(it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); orientationPopup.exec( QCursor::pos() ); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { + mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); + mitk::Image* currSeg = dynamic_cast(segmentationNode->GetData()); + mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); + if (currSeg->GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT) + s2iFilter->SetUShortBinaryPixelType(true); s2iFilter->SetInput(dynamic_cast(m_InterpolatedSurfaceNode->GetData())); // check if ToolManager holds valid ReferenceData if (m_ToolManager->GetReferenceData(0) == NULL || m_ToolManager->GetWorkingData(0) == NULL) { return; } s2iFilter->SetImage(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); s2iFilter->Update(); - mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); mitk::Image::Pointer newSeg = s2iFilter->GetOutput(); - mitk::Image* currSeg = dynamic_cast(segmentationNode->GetData()); unsigned int timestep = m_LastSNC->GetTime()->GetPos(); mitk::ImageReadAccessor readAccess(newSeg, newSeg->GetVolumeData(timestep)); const void* cPointer = readAccess.GetData(); if (currSeg && cPointer) { currSeg->SetVolume( cPointer, timestep, 0 ); } else { return; } m_CmbInterpolation->setCurrentIndex(0); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); mitk::DataNode::Pointer segSurface = mitk::DataNode::New(); float rgb[3]; segmentationNode->GetColor(rgb); segSurface->SetColor(rgb); segSurface->SetData(m_InterpolatedSurfaceNode->GetData()); std::stringstream stream; stream << segmentationNode->GetName(); stream << "_"; stream << "3D-interpolation"; segSurface->SetName(stream.str()); segSurface->SetProperty( "opacity", mitk::FloatProperty::New(0.7) ); segSurface->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(true)); segSurface->SetProperty( "3DInterpolationResult", mitk::BoolProperty::New(true)); segSurface->SetVisibility(false); m_DataStorage->Add(segSurface, segmentationNode); this->Show3DInterpolationResult(false); } } void::QmitkSlicesInterpolator::OnSuggestPlaneClicked() { if (m_PlaneWatcher.isRunning()) m_PlaneWatcher.waitForFinished(); m_PlaneFuture = QtConcurrent::run(this, &QmitkSlicesInterpolator::RunPlaneSuggestion); m_PlaneWatcher.setFuture(m_PlaneFuture); } void::QmitkSlicesInterpolator::RunPlaneSuggestion() { if(m_FirstRun) mitk::ProgressBar::GetInstance()->AddStepsToDo(8); else mitk::ProgressBar::GetInstance()->AddStepsToDo(3); m_EdgeDetector->SetSegmentationMask(m_Segmentation); m_EdgeDetector->SetInput(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); m_EdgeDetector->Update(); mitk::UnstructuredGrid::Pointer uGrid = mitk::UnstructuredGrid::New(); uGrid->SetVtkUnstructuredGrid(m_EdgeDetector->GetOutput()->GetVtkUnstructuredGrid()); mitk::ProgressBar::GetInstance()->Progress(); mitk::Surface::Pointer surface = dynamic_cast(m_InterpolatedSurfaceNode->GetData()); vtkSmartPointer< vtkPolyData > vtkpoly = surface->GetVtkPolyData(); vtkSmartPointer< vtkPoints> vtkpoints = vtkpoly->GetPoints(); vtkSmartPointer vGrid = vtkSmartPointer::New(); vtkSmartPointer verts = vtkSmartPointer::New(); verts->GetPointIds()->SetNumberOfIds(vtkpoints->GetNumberOfPoints()); for(int i=0; iGetNumberOfPoints(); i++) { verts->GetPointIds()->SetId(i,i); } vGrid->Allocate(1); vGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); vGrid->SetPoints(vtkpoints); mitk::UnstructuredGrid::Pointer interpolationGrid = mitk::UnstructuredGrid::New(); interpolationGrid->SetVtkUnstructuredGrid(vGrid); m_PointScorer->SetInput(0, uGrid); m_PointScorer->SetInput(1, interpolationGrid); m_PointScorer->Update(); mitk::UnstructuredGrid::Pointer scoredGrid = mitk::UnstructuredGrid::New(); scoredGrid = m_PointScorer->GetOutput(); mitk::ProgressBar::GetInstance()->Progress(); double spacing = mitk::SurfaceInterpolationController::GetInstance()->GetDistanceImageSpacing(); m_PlaneSuggester->SetInput(scoredGrid); m_PlaneSuggester->SetMinPts(4); m_PlaneSuggester->SetEps(spacing); m_PlaneSuggester->Update(); mitk::GeometryData::Pointer geoData = m_PlaneSuggester->GetGeoData(); mitk::PlaneGeometry::Pointer plane = dynamic_cast(geoData->GetGeometry()); mitk::ProgressBar::GetInstance()->Progress(); mitk::BaseRenderer::Pointer br = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")); br->GetSliceNavigationController()->ReorientSlices(plane->GetOrigin(),plane->GetNormal()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_FirstRun = false; } void QmitkSlicesInterpolator::OnReinit3DInterpolation() { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("3DContourContainer", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer contourNodes = m_DataStorage->GetDerivations( m_ToolManager->GetWorkingData(0), pred); if (contourNodes->Size() != 0) { m_3DContourNode = contourNodes->at(0); } else { QMessageBox errorInfo; errorInfo.setWindowTitle("Reinitialize surface interpolation"); errorInfo.setIcon(QMessageBox::Information); errorInfo.setText("No contours available for the selected segmentation!"); errorInfo.exec(); } mitk::Surface::Pointer contours = dynamic_cast(m_3DContourNode->GetData()); if (contours) mitk::SurfaceInterpolationController::GetInstance()->ReinitializeInterpolation(contours); m_BtnReinit3DInterpolation->setEnabled(false); } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction* action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find( action ); if (iter != ACTION_TO_SLICEDIMENSION.end()) { mitk::SliceNavigationController* slicer = iter->second; AcceptAllInterpolations( slicer ); } } catch(...) { /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); //additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() ) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add( m_FeedbackNode ); } } } catch(...) { // don't care (double add/remove) } if (m_ToolManager) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled( workingNode != NULL ); m_BtnApply2D->setEnabled( on ); m_FeedbackNode->SetVisibility( on ); if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } if (workingNode) { mitk::Image* segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume( segmentation ); if (referenceNode) { mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume( referenceImage ); // may be NULL } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); if( currentColor[2] == SURFACE_COLOR_RGB[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(1.0f,1.0f,1.0f)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; this->CheckSupportedImageDimension(); try { if ( m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); mitk::NodePredicateAnd::Pointer pred = mitk::NodePredicateAnd::New(mitk::NodePredicateProperty::New("3DInterpolationResult", mitk::BoolProperty::New(true)), mitk::NodePredicateDataType::New("Surface")); mitk::DataStorage::SetOfObjects::ConstPointer interpolationResults = m_DataStorage->GetDerivations(workingNode, pred); for (unsigned int i = 0; i < interpolationResults->Size(); ++i) { mitk::DataNode::Pointer currNode = interpolationResults->at(i); if (currNode.IsNotNull()) m_DataStorage->Remove(currNode); } if ((workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) && !isInterpolationResult && m_3DInterpolationEnabled) { int ret = QMessageBox::Yes; if (m_SurfaceInterpolator->EstimatePortionOfNeededMemory() > 0.5) { QMessageBox msgBox; msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!"); msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?"); msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); ret = msgBox.exec(); } if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); if (ret == QMessageBox::Yes) { m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { m_CmbInterpolation->setCurrentIndex(0); } } else if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled); } } else { QWidget::setEnabled( false ); m_ChkShowPositionNodes->setEnabled(m_3DInterpolationEnabled); } } if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled); } } catch(...) { MITK_ERROR<<"Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { if (m_2DInterpolationEnabled && m_LastSNC) { // determine which one is the current view, try to do an initial interpolation mitk::BaseRenderer* renderer = m_LastSNC->GetRenderer(); if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { //TODO 18735: This cast always returns NULL, cuase GetWorldGeometry returns a Base Geometry?!?!?! const mitk::TimeGeometry* timeGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeGeometry) { mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeGeometry), renderer->GetSlice() ); TranslateAndInterpolateChangedSlice(event, m_LastSNC); } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject& /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) { if(m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } void QmitkSlicesInterpolator:: SetCurrentContourListID() { // New ContourList = hide current interpolation Show3DInterpolationResult(false); if ( m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC ) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); if (!isInterpolationResult) { QWidget::setEnabled( true ); // In case the time is not valid use 0 to access the time geometry of the working node unsigned int time_position = 0; if( m_LastSNC->GetTime() != NULL ) time_position = m_LastSNC->GetTime()->GetPos(); mitk::Vector3D spacing = workingNode->GetData()->GetGeometry( time_position )->GetSpacing(); double minSpacing (100); double maxSpacing (0); for (int i =0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); mitk::Image* segmentationImage = dynamic_cast(workingNode->GetData()); /*if (segmentationImage->GetDimension() == 3) {*/ m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage); m_SurfaceInterpolator->SetCurrentTimeStep( time_position ); //} /*else MITK_INFO<<"3D Interpolation is only supported for 3D images at the moment!";*/ if (m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } } else { QWidget::setEnabled(false); } } } void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status) { if (m_InterpolatedSurfaceNode.IsNotNull()) m_InterpolatedSurfaceNode->SetVisibility(status); if (m_3DContourNode.IsNotNull()) m_3DContourNode->SetVisibility(status, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::CheckSupportedImageDimension() { if (m_ToolManager->GetWorkingData(0)) m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); /*if (m_3DInterpolationEnabled && m_Segmentation && m_Segmentation->GetDimension() != 3) { QMessageBox info; info.setWindowTitle("3D Interpolation Process"); info.setIcon(QMessageBox::Information); info.setText("3D Interpolation is only supported for 3D images at the moment!"); info.exec(); m_CmbInterpolation->setCurrentIndex(0); }*/ } void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject& /*e*/) { //Don't know how to avoid const_cast here?! mitk::SliceNavigationController* slicer = dynamic_cast(const_cast(sender)); if (slicer) { m_ControllerToTimeObserverTag.remove(slicer); m_ControllerToSliceObserverTag.remove(slicer); m_ControllerToDeleteObserverTag.remove(slicer); } } diff --git a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp index 6077e449dd..ed55deb9d3 100644 --- a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp +++ b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp @@ -1,334 +1,343 @@ /*=================================================================== 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 "mitkComputeContourSetNormalsFilter.h" #include "mitkImagePixelReadAccessor.h" #include "mitkIOUtil.h" mitk::ComputeContourSetNormalsFilter::ComputeContourSetNormalsFilter() : m_SegmentationBinaryImage(NULL) , m_MaxSpacing(5) , m_NegativeNormalCounter(0) , m_PositiveNormalCounter(0) , m_UseProgressBar(false) , m_ProgressStepSize(1) { mitk::Surface::Pointer output = mitk::Surface::New(); this->SetNthOutput(0, output.GetPointer()); } mitk::ComputeContourSetNormalsFilter::~ComputeContourSetNormalsFilter() { } void mitk::ComputeContourSetNormalsFilter::GenerateData() { unsigned int numberOfInputs = this->GetNumberOfIndexedInputs(); //Iterating over each input for(unsigned int i = 0; i < numberOfInputs; i++) { //Getting the inputs polydata and polygons Surface* currentSurface = const_cast( this->GetInput(i) ); vtkPolyData* polyData = currentSurface->GetVtkPolyData(); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType* cell (NULL); vtkIdType cellSize (0); //The array that contains all the vertex normals of the current polygon vtkSmartPointer normals = vtkSmartPointer::New(); normals->SetNumberOfComponents(3); normals->SetNumberOfTuples(polyData->GetNumberOfPoints()); //If the current contour is an inner contour then the direction is -1 //A contour lies inside another one if the pixel values in the direction of the normal is 1 m_NegativeNormalCounter = 0; m_PositiveNormalCounter = 0; vtkIdType offSet (0); //Iterating over each polygon for( existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { if(cellSize < 3)continue; //First we calculate the current polygon's normal double polygonNormal[3] = {0.0}; double p1[3]; double p2[3]; double v1[3]; double v2[3]; existingPoints->GetPoint(cell[0], p1); unsigned int index = cellSize*0.5; existingPoints->GetPoint(cell[index], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; for (vtkIdType k = 2; k < cellSize; k++) { index = cellSize*0.25; existingPoints->GetPoint(cell[index], p1); index = cellSize*0.75; existingPoints->GetPoint(cell[index], p2); v2[0] = p2[0]-p1[0]; v2[1] = p2[1]-p1[1]; v2[2] = p2[2]-p1[2]; vtkMath::Cross(v1,v2,polygonNormal); if (vtkMath::Norm(polygonNormal) != 0) break; } vtkMath::Normalize(polygonNormal); //Now we start computing the normal for each vertex double vertexNormalTemp[3]; existingPoints->GetPoint(cell[0], p1); existingPoints->GetPoint(cell[1], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; vtkMath::Cross(v1,polygonNormal,vertexNormalTemp); vtkMath::Normalize(vertexNormalTemp); double vertexNormal[3]; for (vtkIdType j = 0; j < cellSize-2; j++) { existingPoints->GetPoint(cell[j+1], p1); existingPoints->GetPoint(cell[j+2], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; vtkMath::Cross(v1,polygonNormal,vertexNormal); vtkMath::Normalize(vertexNormal); double finalNormal[3]; finalNormal[0] = (vertexNormal[0] + vertexNormalTemp[0])*0.5; finalNormal[1] = (vertexNormal[1] + vertexNormalTemp[1])*0.5; finalNormal[2] = (vertexNormal[2] + vertexNormalTemp[2])*0.5; vtkMath::Normalize(finalNormal); //Here we determine the direction of the normal if (m_SegmentationBinaryImage) { Point3D worldCoord; worldCoord[0] = p1[0]+finalNormal[0]*m_MaxSpacing; worldCoord[1] = p1[1]+finalNormal[1]*m_MaxSpacing; worldCoord[2] = p1[2]+finalNormal[2]*m_MaxSpacing; double val = 0.0; - mitk::ImagePixelReadAccessor readAccess(m_SegmentationBinaryImage); itk::Index<3> idx; m_SegmentationBinaryImage->GetGeometry()->WorldToIndex(worldCoord, idx); try { - val = readAccess.GetPixelByIndexSafe(idx); + if (m_SegmentationBinaryImage->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) + { + mitk::ImagePixelReadAccessor readAccess(m_SegmentationBinaryImage); + val = readAccess.GetPixelByIndexSafe(idx); + } + else if (m_SegmentationBinaryImage->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT) + { + mitk::ImagePixelReadAccessor readAccess(m_SegmentationBinaryImage); + val = readAccess.GetPixelByIndexSafe(idx); + } } catch (mitk::Exception e) { // If value is outside the image's region ignore it + MITK_WARN<SetTuple(id,finalNormal); } existingPoints->GetPoint(cell[0], p1); existingPoints->GetPoint(cell[1], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; vtkMath::Cross(v1,polygonNormal,vertexNormal); vtkMath::Normalize(vertexNormal); vertexNormal[0] = (vertexNormal[0] + vertexNormalTemp[0])*0.5; vertexNormal[1] = (vertexNormal[1] + vertexNormalTemp[1])*0.5; vertexNormal[2] = (vertexNormal[2] + vertexNormalTemp[2])*0.5; vtkMath::Normalize(vertexNormal); vtkIdType id = cell[0]; normals->SetTuple(id,vertexNormal); id = cell[cellSize-1]; normals->SetTuple(id,vertexNormal); if(m_NegativeNormalCounter > m_PositiveNormalCounter) { for(vtkIdType n = 0; n < cellSize; n++) { double normal[3]; normals->GetTuple(offSet+n, normal); normal[0] = (-1)*normal[0]; normal[1] = (-1)*normal[1]; normal[2] = (-1)*normal[2]; normals->SetTuple(offSet+n, normal); } } m_NegativeNormalCounter = 0; m_PositiveNormalCounter = 0; offSet += cellSize; }//end for all cells Surface::Pointer surface = this->GetOutput(i); surface->GetVtkPolyData()->GetCellData()->SetNormals(normals); }//end for all inputs //Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize); } mitk::Surface::Pointer mitk::ComputeContourSetNormalsFilter::GetNormalsAsSurface() { //Just for debugging: vtkSmartPointer newPolyData = vtkSmartPointer::New(); vtkSmartPointer newLines = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); unsigned int idCounter (0); //Debug end for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); i++) { Surface* currentSurface = const_cast( this->GetOutput(i) ); vtkPolyData* polyData = currentSurface->GetVtkPolyData(); vtkSmartPointer currentCellNormals = vtkDoubleArray::SafeDownCast(polyData->GetCellData()->GetNormals()); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType* cell (NULL); vtkIdType cellSize (0); for( existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { for ( vtkIdType j = 0; j < cellSize; j++ ) { double currentNormal[3]; currentCellNormals->GetTuple(cell[j], currentNormal); vtkSmartPointer line = vtkSmartPointer::New(); line->GetPointIds()->SetNumberOfIds(2); double newPoint[3]; double p0[3]; existingPoints->GetPoint(cell[j], p0); newPoint[0] = p0[0] + currentNormal[0]; newPoint[1] = p0[1] + currentNormal[1]; newPoint[2] = p0[2] + currentNormal[2]; line->GetPointIds()->SetId(0, idCounter); newPoints->InsertPoint(idCounter, p0); idCounter++; line->GetPointIds()->SetId(1, idCounter); newPoints->InsertPoint(idCounter, newPoint); idCounter++; newLines->InsertNextCell(line); }//end for all points }//end for all cells }//end for all outputs newPolyData->SetPoints(newPoints); newPolyData->SetLines(newLines); newPolyData->BuildCells(); mitk::Surface::Pointer surface = mitk::Surface::New(); surface->SetVtkPolyData(newPolyData); return surface; } void mitk::ComputeContourSetNormalsFilter::SetMaxSpacing(double maxSpacing) { m_MaxSpacing = maxSpacing; } void mitk::ComputeContourSetNormalsFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } void mitk::ComputeContourSetNormalsFilter::Reset() { for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++) { this->PopBackInput(); } this->SetNumberOfIndexedInputs(0); this->SetNumberOfIndexedOutputs(0); mitk::Surface::Pointer output = mitk::Surface::New(); this->SetNthOutput(0, output.GetPointer()); } void mitk::ComputeContourSetNormalsFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::ComputeContourSetNormalsFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp index a2264c20df..91aa429b58 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp @@ -1,158 +1,161 @@ /*=================================================================== 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 "QmitkSurfaceToImageWidget.h" #include #include #include #include #include #include #include +#include #include static const char* const HelpText = "Select an image and a surface above"; QmitkSurfaceToImageWidget::QmitkSurfaceToImageWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) : QmitkSegmentationUtilityWidget(timeNavigationController, parent) { m_Controls.setupUi(this); m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::ImageAndSegmentationPredicate); m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::SurfacePredicate); m_Controls.dataSelectionWidget->SetHelpText(HelpText); this->EnableButtons(false); connect (m_Controls.btnSurface2Image, SIGNAL(pressed()), this, SLOT(OnSurface2ImagePressed())); connect(m_Controls.dataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); if( m_Controls.dataSelectionWidget->GetSelection(0).IsNotNull() && m_Controls.dataSelectionWidget->GetSelection(1).IsNotNull() ) { this->OnSelectionChanged( 0, m_Controls.dataSelectionWidget->GetSelection(0)); } } QmitkSurfaceToImageWidget::~QmitkSurfaceToImageWidget() { } void QmitkSurfaceToImageWidget::EnableButtons(bool enable) { m_Controls.btnSurface2Image->setEnabled(enable); } void QmitkSurfaceToImageWidget::OnSelectionChanged(unsigned int, const mitk::DataNode*) { QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; mitk::DataNode::Pointer imageNode = dataSelectionWidget->GetSelection(0); mitk::DataNode::Pointer surfaceNode = dataSelectionWidget->GetSelection(1); if (imageNode.IsNull() || surfaceNode.IsNull() ) { dataSelectionWidget->SetHelpText(HelpText); this->EnableButtons(false); } else { mitk::Image::Pointer image = dynamic_cast( dataSelectionWidget->GetSelection(0)->GetData() ); mitk::Surface::Pointer surface = dynamic_cast( dataSelectionWidget->GetSelection(1)->GetData() ); if( image->GetTimeSteps() != surface->GetTimeSteps() ) { dataSelectionWidget->SetHelpText("Image and surface are of different size"); this->EnableButtons(false); } else { dataSelectionWidget->SetHelpText(""); this->EnableButtons(); } } } void QmitkSurfaceToImageWidget::OnSurface2ImagePressed() { this->EnableButtons(false); QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; mitk::Image::Pointer image = dynamic_cast( dataSelectionWidget->GetSelection(0)->GetData() ); mitk::Surface::Pointer surface = dynamic_cast( dataSelectionWidget->GetSelection(1)->GetData() ); if( image.IsNull() || surface.IsNull()) { MITK_ERROR << "Selection does not contain an image and/or a surface"; QMessageBox::information( this, "Surface To Image", "Selection does not contain an image and/or a surface", QMessageBox::Ok ); this->EnableButtons(); return; } mitk::Image::Pointer resultImage(nullptr); resultImage = this->ConvertSurfaceToImage( image, surface ); if( resultImage.IsNull() ) { MITK_ERROR << "Convert Surface to binary image failed"; QMessageBox::information( this, "Surface To Image", "Convert Surface to binary image failed", QMessageBox::Ok ); this->EnableButtons(); return; } //create name for result node std::string nameOfResultImage = dataSelectionWidget->GetSelection(0)->GetName(); nameOfResultImage.append("_"); nameOfResultImage.append(dataSelectionWidget->GetSelection(1)->GetName()); //create data node and add to data storage mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); resultNode->SetData( resultImage ); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); - resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); +// resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); dataSelectionWidget->GetDataStorage()->Add(resultNode, dataSelectionWidget->GetSelection(0)); this->EnableButtons(); } -mitk::Image::Pointer QmitkSurfaceToImageWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ) +mitk::LabelSetImage::Pointer QmitkSurfaceToImageWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ) { mitk::ProgressBar::GetInstance()->AddStepsToDo(2); mitk::ProgressBar::GetInstance()->Progress(); mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(surface); surfaceToImageFilter->SetImage(image); try { surfaceToImageFilter->Update(); } catch(itk::ExceptionObject& excpt) { MITK_ERROR << excpt.GetDescription(); return nullptr; } mitk::ProgressBar::GetInstance()->Progress(); mitk::Image::Pointer resultImage = surfaceToImageFilter->GetOutput(); + mitk::LabelSetImage::Pointer multilabelImage = mitk::LabelSetImage::New(); + multilabelImage->InitializeByLabeledImage(resultImage); - return resultImage; + return multilabelImage; } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h index f68206cb8d..71cae691aa 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h @@ -1,67 +1,68 @@ /*=================================================================== 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 QmitkSurfaceToImageWidget_h #define QmitkSurfaceToImageWidget_h #include "../QmitkSegmentationUtilityWidget.h" #include namespace mitk { class Surface; class Image; + class LabelSetImage; } /*! \brief QmitkSurfaceToImageWidget The Tool converts a surface to a binary image. The Method requires a surface and an image, which header information defines the output image. The resulting binary image has the same dimension, size, and Geometry3D as the input image. */ class QmitkSurfaceToImageWidget : public QmitkSegmentationUtilityWidget { Q_OBJECT public: /** @brief Default constructor, including creation of GUI elements and signals/slots connections. */ explicit QmitkSurfaceToImageWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = nullptr); /** @brief Defaul destructor. */ ~QmitkSurfaceToImageWidget(); private slots: /** @brief This slot is called if the selection in the workbench is changed. */ void OnSelectionChanged(unsigned int index, const mitk::DataNode* selection); /** @brief This slot is called if user activates the button to convert a surface into a binary image. */ void OnSurface2ImagePressed(); private: /** @brief Enable buttons if data selction is valid. */ void EnableButtons(bool enable = true); /** @brief Convert a surface into an binary image. */ - itk::SmartPointer ConvertSurfaceToImage( itk::SmartPointer image, itk::SmartPointer surface ); + itk::SmartPointer ConvertSurfaceToImage( itk::SmartPointer image, itk::SmartPointer surface ); Ui::QmitkSurfaceToImageWidgetControls m_Controls; }; #endif