diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp index 96ba360d0f..831816b898 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp @@ -1,97 +1,101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkAutoSegmentationTool.h" #include "mitkImage.h" #include "mitkToolManager.h" #include mitk::AutoSegmentationTool::AutoSegmentationTool() : Tool("dummy"), m_OverwriteExistingSegmentation(false) { } +mitk::AutoSegmentationTool::AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_OverwriteExistingSegmentation(false) +{ +} + mitk::AutoSegmentationTool::~AutoSegmentationTool() { } const char *mitk::AutoSegmentationTool::GetGroup() const { return "autoSegmentation"; } mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimeStep(const mitk::Image* image, unsigned int timestep) { if (nullptr == image) return image; if (image->GetDimension() != 4) return image; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(timestep)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint) { if (nullptr == image) return image; if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; return AutoSegmentationTool::GetImageByTimeStep(image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint)); } void mitk::AutoSegmentationTool::SetOverwriteExistingSegmentation(bool overwrite) { m_OverwriteExistingSegmentation = overwrite; } std::string mitk::AutoSegmentationTool::GetCurrentSegmentationName() { if (m_ToolManager->GetWorkingData(0)) return m_ToolManager->GetWorkingData(0)->GetName(); else return ""; } mitk::DataNode *mitk::AutoSegmentationTool::GetTargetSegmentationNode() { mitk::DataNode::Pointer segmentationNode = m_ToolManager->GetWorkingData(0); if (!m_OverwriteExistingSegmentation) { mitk::DataNode::Pointer refNode = m_ToolManager->GetReferenceData(0); if (refNode.IsNull()) { // TODO create and use segmentation exceptions instead!! MITK_ERROR << "No valid reference data!"; return nullptr; } std::string nodename = refNode->GetName() + "_" + this->GetName(); mitk::Color color; color.SetRed(1); color.SetBlue(0); color.SetGreen(0); //create a new segmentation node based on the current segmentation as template segmentationNode = CreateEmptySegmentationNode(dynamic_cast(segmentationNode->GetData()), nodename, color); m_ToolManager->GetDataStorage()->Add(segmentationNode, refNode); } return segmentationNode; } diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h index dfb36b908c..bb717c23cd 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h @@ -1,71 +1,72 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkAutoSegmentationTool_h_Included #define mitkAutoSegmentationTool_h_Included #include "mitkCommon.h" #include "mitkTool.h" #include namespace mitk { class Image; /** \brief Superclass for tool that create a new segmentation without user interaction in render windows This class is undocumented. Ask the creator ($Author$) to supply useful comments. */ class MITKSEGMENTATION_EXPORT AutoSegmentationTool : public Tool { public: mitkClassMacro(AutoSegmentationTool, Tool); /** This function controls wether a confirmed segmentation should replace the old * segmentation/working node (true) or if it should be stored as new and additional * node (false). */ void SetOverwriteExistingSegmentation(bool overwrite); /** * @brief Gets the name of the currently selected segmentation node * @return the name of the segmentation node or an empty string if * none is selected */ std::string GetCurrentSegmentationName(); /** * @brief Depending on the selected mode either returns the currently selected segmentation * or creates a new one from the selected reference data and adds the new segmentation * to the datastorage * @return a mitk::DataNode which contains a segmentation image */ virtual mitk::DataNode *GetTargetSegmentationNode(); protected: AutoSegmentationTool(); // purposely hidden + AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden ~AutoSegmentationTool() override; const char *GetGroup() const override; /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ static Image::ConstPointer GetImageByTimeStep(const Image* image, unsigned int timestep); /** Helper that extracts the image for the passed time point, if the image has multiple time steps.*/ static Image::ConstPointer GetImageByTimePoint(const Image* image, TimePointType timePoint); bool m_OverwriteExistingSegmentation; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp index e8554c2228..ebb78ea6b9 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp @@ -1,478 +1,519 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkAutoSegmentationWithPreviewTool.h" #include "mitkToolManager.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageTimeSelector.h" #include "mitkLabelSetImage.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkNodePredicateGeometry.h" +#include "mitkSegTool2D.h" mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews): m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = mitk::ToolCommand::New(); } +mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : AutoSegmentationTool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews) +{ + m_ProgressCommand = mitk::ToolCommand::New(); +} + + mitk::AutoSegmentationWithPreviewTool::~AutoSegmentationWithPreviewTool() { } bool mitk::AutoSegmentationWithPreviewTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const { if (!Superclass::CanHandle(referenceData, workingData)) return false; if (workingData == nullptr) return true; auto* labelSet = dynamic_cast(workingData); if (labelSet != nullptr) return true; auto* image = dynamic_cast(workingData); if (image == nullptr) return false; //if it is a normal image and not a label set image is used as working data //it must have the same pixel type as a label set. return MakeScalarPixelType< DefaultSegmentationDataType >() == image->GetPixelType(); } void mitk::AutoSegmentationWithPreviewTool::Activated() { Superclass::Activated(); m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged); m_ToolManager->SelectedTimePointChanged += mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged); m_ReferenceDataNode = m_ToolManager->GetReferenceData(0); m_SegmentationInputNode = m_ReferenceDataNode; m_LastTimePointOfUpdate = 0; if (m_PreviewSegmentationNode.IsNull()) { m_PreviewSegmentationNode = DataNode::New(); m_PreviewSegmentationNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); m_PreviewSegmentationNode->SetProperty("name", StringProperty::New(std::string(this->GetName())+" preview")); m_PreviewSegmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); m_PreviewSegmentationNode->SetProperty("binary", BoolProperty::New(true)); m_PreviewSegmentationNode->SetProperty("helper object", BoolProperty::New(true)); } if (m_SegmentationInputNode.IsNotNull()) { this->ResetPreviewNode(); this->InitiateToolByInput(); } else { m_ToolManager->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged); m_ToolManager->SelectedTimePointChanged -= mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged); m_SegmentationInputNode = nullptr; m_ReferenceDataNode = nullptr; + m_WorkingPlaneGeometry = nullptr; try { if (DataStorage *storage = m_ToolManager->GetDataStorage()) { storage->Remove(m_PreviewSegmentationNode); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (...) { // don't care } if (m_PreviewSegmentationNode.IsNotNull()) { m_PreviewSegmentationNode->SetData(nullptr); } Superclass::Deactivated(); } void mitk::AutoSegmentationWithPreviewTool::ConfirmSegmentation() { if (m_LazyDynamicPreviews && m_CreateAllTimeSteps) { // The tool should create all time steps but is currently in lazy mode, // thus ensure that a preview for all time steps is available. this->UpdatePreview(true); } CreateResultSegmentationFromPreview(); RenderingManager::GetInstance()->RequestUpdateAll(); if (!m_KeepActiveAfterAccept) { m_ToolManager->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::InitiateToolByInput() { //default implementation does nothing. //implement in derived classes to change behavior } mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentation() { if (m_PreviewSegmentationNode.IsNull()) { return nullptr; } return dynamic_cast(m_PreviewSegmentationNode->GetData()); } mitk::DataNode* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentationNode() { return m_PreviewSegmentationNode; } const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetSegmentationInput() const { if (m_SegmentationInputNode.IsNull()) { return nullptr; } return dynamic_cast(m_SegmentationInputNode->GetData()); } const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetReferenceData() const { if (m_ReferenceDataNode.IsNull()) { return nullptr; } return dynamic_cast(m_ReferenceDataNode->GetData()); } void mitk::AutoSegmentationWithPreviewTool::ResetPreviewNode() { itk::RGBPixel previewColor; previewColor[0] = 0.0f; previewColor[1] = 1.0f; previewColor[2] = 0.0f; const auto image = this->GetSegmentationInput(); if (nullptr != image) { mitk::LabelSetImage::ConstPointer workingImage = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (workingImage.IsNotNull()) { auto newPreviewImage = workingImage->Clone(); if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); // Let's paint the feedback node green... newPreviewImage->GetActiveLabel()->SetColor(previewColor); newPreviewImage->GetActiveLabelSet()->UpdateLookupTable(newPreviewImage->GetActiveLabel()->GetValue()); } else { mitk::Image::ConstPointer workingImageBin = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (workingImageBin.IsNotNull()) { auto newPreviewImage = workingImageBin->Clone(); if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage->Clone()); } else { mitkThrow() << "Tool is an invalid state. Cannot setup preview node. Working data is an unsupported class and should have not been accepted by CanHandle()."; } } m_PreviewSegmentationNode->SetColor(previewColor); m_PreviewSegmentationNode->SetOpacity(0.5); int layer(50); m_ReferenceDataNode->GetIntProperty("layer", layer); m_PreviewSegmentationNode->SetIntProperty("layer", layer + 1); if (DataStorage *ds = m_ToolManager->GetDataStorage()) { if (!ds->Exists(m_PreviewSegmentationNode)) ds->Add(m_PreviewSegmentationNode, m_ReferenceDataNode); } } } template static void ITKSetVolume(const itk::Image *originalImage, mitk::Image *segmentation, unsigned int timeStep) { auto constPixelContainer = originalImage->GetPixelContainer(); //have to make a const cast because itk::PixelContainer does not provide a const correct access :( auto pixelContainer = const_cast::PixelContainer*>(constPixelContainer); segmentation->SetVolume((void *)pixelContainer->GetBufferPointer(), timeStep); } void mitk::AutoSegmentationWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep) { try { Image::ConstPointer image3D = this->GetImageByTimeStep(sourceImage, timeStep); if (image3D->GetPixelType() != destinationImage->GetPixelType()) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same pixel type. " << "Source pixel type: " << sourceImage->GetPixelType().GetTypeAsString() << "; destination pixel type: " << destinationImage->GetPixelType().GetTypeAsString(); } if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, false)) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry."; } - if (image3D->GetDimension() == 2) - { - AccessFixedDimensionByItk_2( - image3D, ITKSetVolume, 2, destinationImage, timeStep); + if (nullptr != this->GetWorkingPlaneGeometry()) + { //only transfer a specific slice defined by the working plane + } else - { - AccessFixedDimensionByItk_2( - image3D, ITKSetVolume, 3, destinationImage, timeStep); + { //take care of the full segmentation volume + if (image3D->GetDimension() == 2) + { + AccessFixedDimensionByItk_2( + image3D, ITKSetVolume, 2, destinationImage, timeStep); + } + else + { + AccessFixedDimensionByItk_2( + image3D, ITKSetVolume, 3, destinationImage, timeStep); + } } } catch (...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); throw; } } void mitk::AutoSegmentationWithPreviewTool::CreateResultSegmentationFromPreview() { const auto segInput = this->GetSegmentationInput(); auto previewImage = this->GetPreviewSegmentation(); if (nullptr != segInput && nullptr != previewImage) { DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode(); if (resultSegmentationNode.IsNotNull()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto resultSegmentation = dynamic_cast(resultSegmentationNode->GetData()); // REMARK: the following code in this scope assumes that previewImage and resultSegmentation // are clones of the working image (segmentation provided to the tool). Therefore they have // the same time geometry. if (previewImage->GetTimeSteps() != resultSegmentation->GetTimeSteps()) { mitkThrow() << "Cannot perform threshold. Internal tool state is invalid." << " Preview segmentation and segmentation result image have different time geometries."; } if (m_CreateAllTimeSteps) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } } else { const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } // since we are maybe working on a smaller image, pad it to the size of the original image if (m_ReferenceDataNode.GetPointer() != m_SegmentationInputNode.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, resultSegmentation); padFilter->SetInput(1, dynamic_cast(m_ReferenceDataNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); resultSegmentationNode->SetData(padFilter->GetOutput()); } m_ToolManager->SetWorkingData(resultSegmentationNode); m_ToolManager->GetWorkingData(0)->Modified(); } } } void mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged() { mitk::DataNode::ConstPointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); mitk::Image::Pointer image = dynamic_cast(m_SegmentationInputNode->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_SegmentationInputNode = tmpNode; } else m_SegmentationInputNode = m_ReferenceDataNode; this->ResetPreviewNode(); this->InitiateToolByInput(); this->UpdatePreview(); } void mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged() { if (m_IsTimePointChangeAware && m_PreviewSegmentationNode.IsNotNull() && m_SegmentationInputNode.IsNotNull()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); const bool isStaticSegOnDynamicImage = m_PreviewSegmentationNode->GetData()->GetTimeSteps() == 1 && m_SegmentationInputNode->GetData()->GetTimeSteps() > 1; if (timePoint!=m_LastTimePointOfUpdate && (isStaticSegOnDynamicImage || m_LazyDynamicPreviews)) { //we only need to update either because we are lazzy //or because we have a static segmentation with a dynamic image this->UpdatePreview(); } } } void mitk::AutoSegmentationWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting) { const auto inputImage = this->GetSegmentationInput(); auto previewImage = this->GetPreviewSegmentation(); int progress_steps = 200; this->CurrentlyBusy.Send(true); + m_IsUpdating = true; this->UpdatePrepare(); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { if (nullptr != inputImage && nullptr != previewImage) { m_ProgressCommand->AddStepsToDo(progress_steps); if (previewImage->GetTimeSteps() > 1 && (ignoreLazyPreviewSetting || !m_LazyDynamicPreviews)) { for (unsigned int timeStep = 0; timeStep < inputImage->GetTimeSteps(); ++timeStep) { - auto feedBackImage3D = this->GetImageByTimeStep(inputImage, timeStep); - - this->DoUpdatePreview(feedBackImage3D, previewImage, timeStep); + Image::ConstPointer feedBackImage; + + if (nullptr != this->GetWorkingPlaneGeometry()) + { //only extract a specific slice defined by the working plane as feedback image. + feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, timeStep); + } + else + { //work on the whole feedback image + feedBackImage = this->GetImageByTimeStep(inputImage, timeStep); + } + + this->DoUpdatePreview(feedBackImage, previewImage, timeStep); } } else { - auto feedBackImage3D = this->GetImageByTimePoint(inputImage, timePoint); + Image::ConstPointer feedBackImage; + if (nullptr != this->GetWorkingPlaneGeometry()) + { + feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint); + } + else + { + feedBackImage = this->GetImageByTimePoint(inputImage, timePoint); + } + auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); - this->DoUpdatePreview(feedBackImage3D, previewImage, timeStep); + this->DoUpdatePreview(feedBackImage, previewImage, timeStep); } RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (itk::ExceptionObject & excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); m_ProgressCommand->SetProgress(progress_steps); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); } catch (...) { m_ProgressCommand->SetProgress(progress_steps); + m_IsUpdating = false; CurrentlyBusy.Send(false); throw; } this->UpdateCleanUp(); m_LastTimePointOfUpdate = timePoint; m_ProgressCommand->SetProgress(progress_steps); + m_IsUpdating = false; CurrentlyBusy.Send(false); } +bool mitk::AutoSegmentationWithPreviewTool::IsUpdating() const +{ + return m_IsUpdating; +} + void mitk::AutoSegmentationWithPreviewTool::UpdatePrepare() { // default implementation does nothing //reimplement in derived classes for special behavior } void mitk::AutoSegmentationWithPreviewTool::UpdateCleanUp() { // default implementation does nothing //reimplement in derived classes for special behavior } mitk::TimePointType mitk::AutoSegmentationWithPreviewTool::GetLastTimePointOfUpdate() const { return m_LastTimePointOfUpdate; } diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h index 848ff0c005..b3cad84572 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h @@ -1,152 +1,169 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkAutoSegmentationWithPreviewTool_h_Included #define mitkAutoSegmentationWithPreviewTool_h_Included #include "mitkAutoSegmentationTool.h" #include "mitkCommon.h" #include "mitkDataNode.h" #include "mitkToolCommand.h" #include namespace mitk { /** \brief Base class for any auto segmentation tool that provides a preview of the new segmentation. This tool class implements a lot basic logic to handle auto segmentation tools with preview, Time point and ROI support. Derived classes will ask to update the segmentation preview if needed (e.g. because the ROI or the current time point has changed) or because derived tools indicated the need to update themselves. This class also takes care to properly transfer a confirmed preview into the segementation result. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ class MITKSEGMENTATION_EXPORT AutoSegmentationWithPreviewTool : public AutoSegmentationTool { public: mitkClassMacro(AutoSegmentationWithPreviewTool, AutoSegmentationTool); void Activated() override; void Deactivated() override; void ConfirmSegmentation(); itkSetMacro(CreateAllTimeSteps, bool); itkGetMacro(CreateAllTimeSteps, bool); itkBooleanMacro(CreateAllTimeSteps); itkSetMacro(KeepActiveAfterAccept, bool); itkGetMacro(KeepActiveAfterAccept, bool); itkBooleanMacro(KeepActiveAfterAccept); itkSetMacro(IsTimePointChangeAware, bool); itkGetMacro(IsTimePointChangeAware, bool); itkBooleanMacro(IsTimePointChangeAware); bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; /** Triggers the actualization of the preview * @param ignoreLazyPreviewSetting If set true UpdatePreview will always * generate the preview for all time steps. If set to false, UpdatePreview * will regard the setting specified by the constructor. * To define the update generation for time steps implement DoUpdatePreview. * To alter what should be done directly before or after the update of the preview, * reimplement UpdatePrepare() or UpdateCleanUp().*/ void UpdatePreview(bool ignoreLazyPreviewSetting = false); + /** Indicate if currently UpdatePreview is triggered (true) or not (false).*/ + bool IsUpdating() const; + protected: mitk::ToolCommand::Pointer m_ProgressCommand; /** Member is always called if GetSegmentationInput() has changed * (e.g. because a new ROI was defined, or on activation) to give derived * classes the posibility to initiate their state accordingly. * Reimplement this function to implement special behavior. */ virtual void InitiateToolByInput(); virtual void UpdatePrepare(); virtual void UpdateCleanUp(); /** This function does the real work. Here the preview for a given * input image should be computed and stored in the also passed * preview image at the passed time step. */ virtual void DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) = 0; - AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews = false); + AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews = false); // purposely hidden + AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden + ~AutoSegmentationWithPreviewTool() override; /** Returns the image that contains the preview of the current segmentation. * Returns null if the node is not set or does not contain an image.*/ Image* GetPreviewSegmentation(); DataNode* GetPreviewSegmentationNode(); /** Returns the input that should be used for any segmentation/preview or tool update. * It is either the data of ReferenceDataNode itself or a part of it defined by a ROI mask * provided by the tool manager. Derived classes should regard this as the relevant * input data for any processing. * Returns null if the node is not set or does not contain an image.*/ const Image* GetSegmentationInput() const; /** Returns the image that is provided by the ReferenceDataNode. * Returns null if the node is not set or does not contain an image.*/ const Image* GetReferenceData() const; /** Resets the preview node so it is empty and ready to be filled by the tool*/ void ResetPreviewNode(); TimePointType GetLastTimePointOfUpdate() const; + itkSetObjectMacro(WorkingPlaneGeometry, PlaneGeometry); + itkGetConstObjectMacro(WorkingPlaneGeometry, PlaneGeometry); + private: void TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep); void CreateResultSegmentationFromPreview(); void OnRoiDataChanged(); void OnTimePointChanged(); /** Node that containes the preview data generated and managed by this class or derived ones.*/ DataNode::Pointer m_PreviewSegmentationNode; /** The reference data recieved from ToolManager::GetReferenceData when tool was activated.*/ DataNode::Pointer m_ReferenceDataNode; /** Node that containes the data that should be used as input for any auto segmentation. It might * be the same like m_ReferenceDataNode (if no ROI is set) or a sub region (if ROI is set).*/ DataNode::Pointer m_SegmentationInputNode; /** Indicates if Accepting the threshold should transfer/create the segmentations of all time steps (true) or only of the currently selected timepoint (false).*/ bool m_CreateAllTimeSteps = false; /** Indicates if the tool should kept active after accepting the segmentation or not.*/ bool m_KeepActiveAfterAccept = false; /** Relevant if the working data / preview image has multiple time steps (dynamic segmentations). * This flag has to be set by derived classes accordingly to there way to generate dynamic previews. * If LazyDynamicPreview is true, the tool generates only the preview for the current time step. * Therefore it always has to update the preview if current time point has changed and it has to (re)compute * all timeframes if ConfirmSegmentation() is called.*/ bool m_LazyDynamicPreviews = false; bool m_IsTimePointChangeAware = true; TimePointType m_LastTimePointOfUpdate = 0.; + + bool m_IsUpdating = false; + + /** This variable indicates if for the tool a working plane geometry is defined. + * If a working plane is defined the tool will only work an the slice of the input + * and the segmentation. Thus only the relevant input slice will be passed to + * DoUpdatePreview(...) and only the relevant slice of the preview will be transfered when + * ConfirmSegmentation() is called.*/ + PlaneGeometry::Pointer m_WorkingPlaneGeometry; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp new file mode 100644 index 0000000000..44cabc685a --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp @@ -0,0 +1,318 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkFastMarchingBaseTool.h" +#include "mitkToolManager.h" + +#include "mitkBaseRenderer.h" +#include "mitkInteractionConst.h" +#include "mitkRenderingManager.h" +#include "mitkInteractionPositionEvent.h" + +#include "mitkImageAccessByItk.h" + +// itk filter +#include "itkBinaryThresholdImageFilter.h" +#include "itkCurvatureAnisotropicDiffusionImageFilter.h" +#include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" +#include "itkSigmoidImageFilter.h" + +// us +#include +#include +#include +#include + + +mitk::FastMarchingBaseTool::FastMarchingBaseTool() + : AutoSegmentationWithPreviewTool("FastMarchingTool"), + m_LowerThreshold(0), + m_UpperThreshold(200), + m_StoppingValue(100), + m_Sigma(1.0), + m_Alpha(-0.5), + m_Beta(3.0) +{ +} + +mitk::FastMarchingBaseTool::~FastMarchingBaseTool() +{ +} + +bool mitk::FastMarchingBaseTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const +{ + if(!Superclass::CanHandle(referenceData, workingData)) + return false; + + if (referenceData == nullptr) + return false; + + auto *image = dynamic_cast(referenceData); + + if (image == nullptr) + return false; + + if (image->GetDimension() < 3) + return false; + + return true; +} + +const char **mitk::FastMarchingBaseTool::GetXPM() const +{ + return nullptr; // mitkFastMarchingBaseTool_xpm; +} + +us::ModuleResource mitk::FastMarchingBaseTool::GetIconResource() const +{ + us::Module *module = us::GetModuleContext()->GetModule(); + us::ModuleResource resource = module->GetResource("FastMarching_48x48.png"); + return resource; +} + +us::ModuleResource mitk::FastMarchingBaseTool::GetCursorIconResource() const +{ + us::Module* module = us::GetModuleContext()->GetModule(); + us::ModuleResource resource = module->GetResource("FastMarching_Cursor_32x32.png"); + return resource; +} + +void mitk::FastMarchingBaseTool::SetUpperThreshold(double value) +{ + m_UpperThreshold = value / 10.0; +} + +void mitk::FastMarchingBaseTool::SetLowerThreshold(double value) +{ + m_LowerThreshold = value / 10.0; +} + +void mitk::FastMarchingBaseTool::SetBeta(double value) +{ + if (m_Beta != value) + { + m_Beta = value; + } +} + +void mitk::FastMarchingBaseTool::SetSigma(double value) +{ + if (m_Sigma != value) + { + if (value > 0.0) + { + m_Sigma = value; + } + } +} + +void mitk::FastMarchingBaseTool::SetAlpha(double value) +{ + if (m_Alpha != value) + { + m_Alpha = value; + } +} + +void mitk::FastMarchingBaseTool::SetStoppingValue(double value) +{ + if (m_StoppingValue != value) + { + m_StoppingValue = value; + } +} + +void mitk::FastMarchingBaseTool::Activated() +{ + Superclass::Activated(); + + m_SeedsAsPointSet = mitk::PointSet::New(); + m_SeedsAsPointSetNode = mitk::DataNode::New(); + m_SeedsAsPointSetNode->SetData(m_SeedsAsPointSet); + m_SeedsAsPointSetNode->SetName(std::string(this->GetName()) + "_PointSet"); + m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); + m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); + m_SeedsAsPointSetNode->SetVisibility(true); + + m_ToolManager->GetDataStorage()->Add(m_SeedsAsPointSetNode, m_ToolManager->GetWorkingData(0)); +} + +void mitk::FastMarchingBaseTool::Deactivated() +{ + this->ClearSeeds(); + + m_ToolManager->GetDataStorage()->Remove(m_SeedsAsPointSetNode); + m_SeedsAsPointSetNode = nullptr; + m_SeedsAsPointSet = nullptr; + + Superclass::Deactivated(); +} + +void mitk::FastMarchingBaseTool::ConnectActionsAndFunctions() +{ + CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); + CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); + CONNECT_FUNCTION("DeletePoint", OnDelete); +} + +void mitk::FastMarchingBaseTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) +{ + if (!this->IsUpdating() && m_SeedsAsPointSet.IsNotNull()) + { + const auto positionEvent = dynamic_cast(interactionEvent); + + if (positionEvent != nullptr) + { + auto newWorkingPlaneGeometry = positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone(); + + // if click was on another plane and we are in 2D mode wie should reset the seeds + if (TOOL_DIMENSION == 2 && ( nullptr == this->GetWorkingPlaneGeometry() || !this->GetWorkingPlaneGeometry()->IsOnPlane(newWorkingPlaneGeometry.GetPointer()))) + { + this->ClearSeeds(); + this->SetWorkingPlaneGeometry(newWorkingPlaneGeometry); + } + + m_SeedsAsPointSet->InsertPoint(m_SeedsAsPointSet->GetSize(), positionEvent->GetPositionInWorld()); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + this->UpdatePreview(); + } + } +} + +void mitk::FastMarchingBaseTool::OnDelete(StateMachineAction*, InteractionEvent* interactionEvent) +{ + if (!this->IsUpdating() && m_SeedsAsPointSet.IsNotNull()) + { + // delete last seed point + if (this->m_SeedsAsPointSet->GetSize() > 0) + { + // delete last point in pointset - somehow ugly + m_SeedsAsPointSet->GetPointSet()->GetPoints()->DeleteIndex(m_SeedsAsPointSet->GetSize() - 1); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + this->UpdatePreview(); + } + } +} + +void mitk::FastMarchingBaseTool::ClearSeeds() +{ + if (this->m_SeedsAsPointSet.IsNotNull()) + { + // renew pointset + this->m_SeedsAsPointSet = mitk::PointSet::New(); + this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); + } +} + +template +void mitk::FastMarchingBaseTool::DoITKFastMarching(const itk::Image* inputImage, + mitk::Image* previewImage, unsigned int timeStep, const mitk::BaseGeometry* inputGeometry) +{ + // typedefs for itk pipeline + typedef itk::Image InputImageType; + + typedef float InternalPixelType; + typedef itk::Image InternalImageType; + + typedef mitk::Tool::DefaultSegmentationDataType OutputPixelType; + typedef itk::Image OutputImageType; + + typedef itk::CurvatureAnisotropicDiffusionImageFilter SmoothingFilterType; + typedef itk::GradientMagnitudeRecursiveGaussianImageFilter GradientFilterType; + typedef itk::SigmoidImageFilter SigmoidFilterType; + typedef itk::BinaryThresholdImageFilter ThresholdingFilterType; + + typedef itk::FastMarchingImageFilter FastMarchingFilterType; + typedef FastMarchingFilterType::NodeContainer NodeContainer; + typedef FastMarchingFilterType::NodeType NodeType; + + //convert point set seed into trialpoint + NodeContainer::Pointer trialPoints = NodeContainer::New(); + trialPoints->Initialize(); + + for (auto pos = m_SeedsAsPointSet->Begin(); pos != m_SeedsAsPointSet->End(); ++pos) + { + mitk::Point3D clickInIndex; + + inputGeometry->WorldToIndex(pos->Value(), clickInIndex); + itk::Index seedPosition; + for (unsigned int dim = 0; dim < VImageDimension; ++dim) + { + seedPosition[dim] = clickInIndex[dim]; + } + + NodeType node; + const double seedValue = 0.0; + node.SetValue(seedValue); + node.SetIndex(seedPosition); + trialPoints->InsertElement(trialPoints->Size(), node); + } + + // assemble pipeline + auto smoothFilter = SmoothingFilterType::New(); + smoothFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + smoothFilter->SetTimeStep(0.05); + smoothFilter->SetNumberOfIterations(2); + smoothFilter->SetConductanceParameter(9.0); + + auto gradientMagnitudeFilter = GradientFilterType::New(); + gradientMagnitudeFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + gradientMagnitudeFilter->SetSigma(m_Sigma); + + auto sigmoidFilter = SigmoidFilterType::New(); + sigmoidFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + sigmoidFilter->SetAlpha(m_Alpha); + sigmoidFilter->SetBeta(m_Beta); + sigmoidFilter->SetOutputMinimum(0.0); + sigmoidFilter->SetOutputMaximum(1.0); + + auto fastMarchingFilter = FastMarchingFilterType::New(); + fastMarchingFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + fastMarchingFilter->SetStoppingValue(m_StoppingValue); + fastMarchingFilter->SetTrialPoints(trialPoints); + + auto thresholdFilter = ThresholdingFilterType::New(); + thresholdFilter->SetLowerThreshold(m_LowerThreshold); + thresholdFilter->SetUpperThreshold(m_UpperThreshold); + thresholdFilter->SetOutsideValue(0); + thresholdFilter->SetInsideValue(1.0); + + // set up pipeline + smoothFilter->SetInput(inputImage); + gradientMagnitudeFilter->SetInput(smoothFilter->GetOutput()); + sigmoidFilter->SetInput(gradientMagnitudeFilter->GetOutput()); + fastMarchingFilter->SetInput(sigmoidFilter->GetOutput()); + thresholdFilter->SetInput(fastMarchingFilter->GetOutput()); + thresholdFilter->Update(); + + if (nullptr == this->GetWorkingPlaneGeometry()) + { + previewImage->SetVolume((void*)(thresholdFilter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); + } + else + { + //TODO -> rework SegTool2D to make it usable here + //this->WriteBackSegmentationResult(m_WorkingPlane, segmentationResult, workingImageTimeStep); + } +} + +void mitk::FastMarchingBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) +{ + if (nullptr != inputAtTimeStep && nullptr != previewImage && m_SeedsAsPointSet.IsNotNull() && m_SeedsAsPointSet->GetSize()>0) + { + AccessFixedDimensionByItk_n(inputAtTimeStep, DoITKFastMarching, 3, (previewImage, timeStep, inputAtTimeStep->GetGeometry())); + } +} diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.h b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.h new file mode 100644 index 0000000000..05f2542f9d --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.h @@ -0,0 +1,113 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkFastMarchingBaseTool_h_Included +#define mitkFastMarchingBaseTool_h_Included + +#include "mitkAutoSegmentationWithPreviewTool.h" +#include "mitkDataNode.h" +#include "mitkPointSet.h" +#include "mitkPointSetDataInteractor.h" +#include "mitkToolCommand.h" + +#include "itkImage.h" +#include "itkFastMarchingImageFilter.h" + +#include + +namespace us +{ + class ModuleResource; +} + +namespace mitk +{ + /** + \brief FastMarching semgentation tool base class. + + 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 FastMarchingBaseTool : public AutoSegmentationWithPreviewTool + { + public: + mitkClassMacro(FastMarchingBaseTool, AutoSegmentationWithPreviewTool); + + bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; + + /* icon stuff */ + const char **GetXPM() const override; + us::ModuleResource GetCursorIconResource() const override; + us::ModuleResource GetIconResource() const override; + + void Activated() override; + void Deactivated() 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 Clear all seed points. + void ClearSeeds(); + + const unsigned int TOOL_DIMENSION = 3; + + protected: + FastMarchingBaseTool(); + ~FastMarchingBaseTool() override; + + void ConnectActionsAndFunctions() override; + + /// \brief Add point action of StateMachine pattern + virtual void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent); + + /// \brief Delete action of StateMachine pattern + virtual void OnDelete(StateMachineAction*, InteractionEvent* interactionEvent); + + void DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) override; + + template + void DoITKFastMarching(const itk::Image* inputImage, + mitk::Image* segmentation, unsigned int timeStep, const mitk::BaseGeometry* inputGeometry); + + 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 + + mitk::DataNode::Pointer m_SeedsAsPointSetNode; // used to visualize the seed points + mitk::PointSet::Pointer m_SeedsAsPointSet; + }; + +} // namespace + +#endif diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 3bce31c9b5..e54cc10e02 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,118 +1,119 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkContourModelSetToImageFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDiffSliceOperation.cpp Algorithms/mitkDiffSliceOperationApplier.cpp Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp #Algorithms/mitkImageToContourModelFilter.cpp Algorithms/mitkImageToLiveWireContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkOverwriteSliceImageFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkVtkImageOverwrite.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkToolManager.cpp Controllers/mitkSegmentationModuleActivator.cpp Controllers/mitkToolManagerProvider.cpp DataManagement/mitkContour.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAdaptiveRegionGrowingTool.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkAutoSegmentationWithPreviewTool.cpp Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp Interactions/mitkBinaryThresholdBaseTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp Interactions/mitkLiveWireTool2D.cpp Interactions/mitkContourTool.cpp Interactions/mitkCorrectorTool2D.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp + Interactions/mitkFastMarchingBaseTool.cpp Interactions/mitkFastMarchingTool.cpp Interactions/mitkFastMarchingTool3D.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkPixelManipulationTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp Interactions/mitkWatershedTool.cpp Interactions/mitkPickingTool.cpp Interactions/mitkSegmentationInteractor.cpp #SO Rendering/mitkContourMapper2D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp #Added from ML Controllers/mitkSliceBasedInterpolationController.cpp Algorithms/mitkSurfaceStampImageFilter.cpp ) set(RESOURCE_FILES Add_48x48.png Add_Cursor_32x32.png Correction_48x48.png Correction_Cursor_32x32.png Erase_48x48.png Erase_Cursor_32x32.png FastMarching_48x48.png FastMarching_Cursor_32x32.png Fill_48x48.png Fill_Cursor_32x32.png LiveWire_48x48.png LiveWire_Cursor_32x32.png Otsu_48x48.png Paint_48x48.png Paint_Cursor_32x32.png Pick_48x48.png RegionGrowing_48x48.png RegionGrowing_Cursor_32x32.png Subtract_48x48.png Subtract_Cursor_32x32.png Threshold_48x48.png TwoThresholds_48x48.png Watershed_48x48.png Watershed_Cursor_32x32.png Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml Interactions/LiveWireTool.xml Interactions/FastMarchingTool.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationToolsConfig.xml Interactions/ContourModelModificationConfig.xml Interactions/ContourModelModificationInteractor.xml )