diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h index b43386aefb..7c567a697e 100644 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h @@ -1,117 +1,124 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkContourModelUtils_h #define mitkContourModelUtils_h #include #include #include #include namespace mitk { /** * \brief Helpful methods for working with contours and images * * */ class MITKCONTOURMODEL_EXPORT ContourModelUtils : public itk::Object { public: mitkClassMacroItkParent(ContourModelUtils, itk::Object); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param slice \param contourIn3D \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) \param constrainToInside */ static ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice, const ContourModel *contourIn3D, bool correctionForIpSegmentation, bool constrainToInside); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param sliceGeometry \param contourIn2D \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry, const ContourModel *contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value. This version always uses the contour of time step 0 and fills the image. \pre sliceImage points to a valid instance \pre projectedContour points to a valid instance */ static void FillContourInSlice(const ContourModel *projectedContour, Image *sliceImage, const Image* workingImage, int paintingPixelValue = 1); /** \brief Fill a contour in a 2D slice with a specified pixel value. This overloaded version uses the contour at the passed contourTimeStep to fill the passed image slice. \pre sliceImage points to a valid instance \pre projectedContour points to a valid instance */ static void FillContourInSlice(const ContourModel *projectedContour, TimeStepType contourTimeStep, Image *sliceImage, const Image* workingImage, int paintingPixelValue = 1); /** - \brief Fills a image (filledImage) into another image (resultImage) by incorporating the rules of LabelSet-Images + \brief Fills the paintingPixelValue into every pixel of resultImage as indicated by filledImage. + If a LableSet image is specified it also by incorporating the rules of LabelSet images when filling the content. + \param filledImage Pointer to the image content that should be checked to decied of a pixel in resultImage should + be filled with paintingPixelValue or not. + \param resultImage Pointer to the image content that should be overwritten guided by the content of filledImage. + \param image Pointer to an mitk image that allows to define the LabelSet image which states steer the filling process. + If an LabelSet instance is passed its states (e.g. locked labels etc...) will be used. If nullptr or an normal image + is passed, then simply any pixel position indicated by filledImage will be overwritten. \param paintingPixelValue the pixelvalue/label that should be used in the result image when filling. \param fillForegroundThreshold The threshold value that decides if a pixel in the filled image counts as foreground (>=fillForegroundThreshold) or not. */ static void FillSliceInSlice(vtkSmartPointer filledImage, vtkSmartPointer resultImage, const Image* image, int paintingPixelValue, double fillForegroundThreshold = 1.0); /** \brief Move the contour in time step 0 to to a new contour model at the given time step. */ static ContourModel::Pointer MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType timeStep); /** \brief Retrieves the active pixel value of a (labelset) image. If the image is basic image, the pixel value 1 (one) will be returned. If the image is actually a labelset image, the pixel value of the active label of the active layer will be returned. \param workingImage The (labelset) image to retrieve the active pixel value of. */ static int GetActivePixelValue(const Image* workingImage); protected: ContourModelUtils(); ~ContourModelUtils() override; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp index b3c883c37a..e19770ed6a 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp @@ -1,549 +1,549 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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(); this->GetToolManager()->RoiDataChanged += mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged); this->GetToolManager()->SelectedTimePointChanged += mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged); m_ReferenceDataNode = this->GetToolManager()->GetReferenceData(0); m_SegmentationInputNode = m_ReferenceDataNode; m_LastTimePointOfUpdate = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); 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 { this->GetToolManager()->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::Deactivated() { this->GetToolManager()->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged); this->GetToolManager()->SelectedTimePointChanged -= mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged); m_SegmentationInputNode = nullptr; m_ReferenceDataNode = nullptr; m_WorkingPlaneGeometry = nullptr; try { if (DataStorage *storage = this->GetToolManager()->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) { this->GetToolManager()->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(this->GetToolManager()->GetWorkingData(0)->GetData()); if (workingImage.IsNotNull()) { auto newPreviewImage = workingImage->Clone(); if (this->GetResetsToEmptyPreview()) { newPreviewImage->ClearBuffer(); } 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(this->GetToolManager()->GetWorkingData(0)->GetData()); if (workingImageBin.IsNotNull()) { mitk::Image::Pointer newPreviewImage; if (this->GetResetsToEmptyPreview()) { newPreviewImage = mitk::Image::New(); newPreviewImage->Initialize(workingImageBin); } else { auto newPreviewImage = workingImageBin->Clone(); } if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); } 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 = this->GetToolManager()->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 sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep); if (sourceImageAtTimeStep->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_COORDINATE_PRECISION, NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, false)) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry."; } if (nullptr != this->GetWorkingPlaneGeometry()) { auto sourceSlice = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), sourceImage, timeStep); SegTool2D::WriteBackSegmentationResult(this->GetTargetSegmentationNode(), m_WorkingPlaneGeometry, sourceSlice, timeStep); } else { //take care of the full segmentation volume if (sourceImageAtTimeStep->GetDimension() == 2) { AccessFixedDimensionByItk_2( sourceImageAtTimeStep, ITKSetVolume, 2, destinationImage, timeStep); } else { AccessFixedDimensionByItk_2( sourceImageAtTimeStep, 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) { this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } } else { const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->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()); } if (m_OverwriteExistingSegmentation) - { //if we overwrite the segmentation (an not just store it as a new result - //in the data storage) we actualize als the tool manager state. + { //if we overwrite the segmentation (and not just store it as a new result + //in the data storage) we update also the tool manager state. this->GetToolManager()->SetWorkingData(resultSegmentationNode); this->GetToolManager()->GetWorkingData(0)->Modified(); } this->EnsureTargetSegmentationNodeInDataStorage(); } } } void mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged() { mitk::DataNode::ConstPointer node = this->GetToolManager()->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; const auto workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); 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 < previewImage->GetTimeSteps(); ++timeStep) { Image::ConstPointer feedBackImage; Image::ConstPointer currentSegImage; auto previewTimePoint = previewImage->GetTimeGeometry()->TimeStepToTimePoint(timeStep); auto inputTimeStep = inputImage->GetTimeGeometry()->TimePointToTimeStep(previewTimePoint); if (nullptr != this->GetWorkingPlaneGeometry()) { //only extract a specific slice defined by the working plane as feedback image. feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, inputTimeStep); currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, previewTimePoint); } else { //work on the whole feedback image feedBackImage = this->GetImageByTimeStep(inputImage, inputTimeStep); currentSegImage = this->GetImageByTimePoint(workingImage, previewTimePoint); } this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep); } } else { Image::ConstPointer feedBackImage; Image::ConstPointer currentSegImage; if (nullptr != this->GetWorkingPlaneGeometry()) { feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint); currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, timePoint); } else { feedBackImage = this->GetImageByTimePoint(inputImage, timePoint); currentSegImage = this->GetImageByTimePoint(workingImage, timePoint); } auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->DoUpdatePreview(feedBackImage, currentSegImage, 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/mitkFastMarchingBaseTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp index 322f768302..e74291e8a9 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp @@ -1,336 +1,330 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkFastMarchingBaseTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "mitkInteractionPositionEvent.h" #include "mitkImageAccessByItk.h" #include "mitkSegTool2D.h" #include // itk filter #include "itkBinaryThresholdImageFilter.h" #include "itkCurvatureAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkSigmoidImageFilter.h" #include "itkFastMarchingImageFilter.h" // us #include #include #include #include mitk::FastMarchingBaseTool::FastMarchingBaseTool(unsigned int toolDim) : AutoSegmentationWithPreviewTool(false, "FastMarchingTool"), m_LowerThreshold(0), m_UpperThreshold(200), m_StoppingValue(100), m_Sigma(1.0), m_Alpha(-0.5), m_Beta(3.0), m_ToolDimension(toolDim) { } 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(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_SeedsAsPointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); 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); this->GetDataStorage()->Add(m_SeedsAsPointSetNode, this->GetToolManager()->GetWorkingData(0)); } void mitk::FastMarchingBaseTool::Deactivated() { this->ClearSeeds(); this->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 workingPlaneGeometry = positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); // if click was on another plane and we are in 2D mode we should reset the seeds if (m_ToolDimension == 2 && ( nullptr == this->GetWorkingPlaneGeometry() || !this->GetWorkingPlaneGeometry()->IsOnPlane(workingPlaneGeometry))) { this->ClearSeeds(); this->SetWorkingPlaneGeometry(workingPlaneGeometry->Clone()); } 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) { m_SeedsAsPointSet->RemovePointAtEnd(0); - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - this->UpdatePreview(); } } } void mitk::FastMarchingBaseTool::ClearSeeds() { if (this->m_SeedsAsPointSet.IsNotNull()) { // renew pointset this->m_SeedsAsPointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_SeedsAsPointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); } } template void mitk::FastMarchingBaseTool::DoITKFastMarching(const itk::Image* inputImage, Image* previewImage, unsigned int timeStep, const 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 typename FastMarchingFilterType::NodeContainer NodeContainer; typedef typename FastMarchingFilterType::NodeType NodeType; //convert point set seed into trialpoint typename 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 { mitk::Image::Pointer sliceImage = mitk::Image::New(); mitk::CastToMitkImage(thresholdFilter->GetOutput(), sliceImage); SegTool2D::WriteSliceToVolume(previewImage, this->GetWorkingPlaneGeometry(), sliceImage, timeStep, false); } } void mitk::FastMarchingBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, Image* previewImage, TimeStepType timeStep) { if (nullptr != inputAtTimeStep && nullptr != previewImage && m_SeedsAsPointSet.IsNotNull() && m_SeedsAsPointSet->GetSize()>0) { if (nullptr == this->GetWorkingPlaneGeometry()) { AccessFixedDimensionByItk_n(inputAtTimeStep, DoITKFastMarching, 3, (previewImage, timeStep, inputAtTimeStep->GetGeometry())); } else { AccessFixedDimensionByItk_n(inputAtTimeStep, DoITKFastMarching, 2, (previewImage, timeStep, inputAtTimeStep->GetGeometry())); } } } diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp index c5ecfa6897..52ab30dfad 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp @@ -1,221 +1,215 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPickingTool.h" #include "mitkProperties.h" #include "mitkToolManager.h" #include "mitkInteractionPositionEvent.h" // us #include #include #include #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkImageTimeSelector.h" #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PickingTool, "PickingTool"); } mitk::PickingTool::PickingTool() : AutoSegmentationWithPreviewTool(false, "PressMoveReleaseAndPointSetting") { this->ResetsToEmptyPreviewOn(); } mitk::PickingTool::~PickingTool() { } bool mitk::PickingTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const { if (!Superclass::CanHandle(referenceData,workingData)) return false; auto* image = dynamic_cast(referenceData); if (image == nullptr) return false; return true; } const char **mitk::PickingTool::GetXPM() const { return nullptr; } 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() { Superclass::Activated(); m_PointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->SetData(m_PointSet); m_PointSetNode->SetName(std::string(this->GetName()) + "_PointSet"); m_PointSetNode->SetBoolProperty("helper object", true); m_PointSetNode->SetColor(0.0, 1.0, 0.0); m_PointSetNode->SetVisibility(true); this->GetDataStorage()->Add(m_PointSetNode, this->GetToolManager()->GetWorkingData(0)); } void mitk::PickingTool::Deactivated() { this->ClearSeeds(); // remove from data storage and disable interaction GetDataStorage()->Remove(m_PointSetNode); m_PointSetNode = nullptr; m_PointSet = nullptr; Superclass::Deactivated(); } void mitk::PickingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } void mitk::PickingTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) { if (!this->IsUpdating() && m_PointSet.IsNotNull()) { const auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { m_PointSet->InsertPoint(m_PointSet->GetSize(), positionEvent->GetPositionInWorld()); - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - this->UpdatePreview(); } } } void mitk::PickingTool::OnDelete(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { if (!this->IsUpdating() && m_PointSet.IsNotNull()) { // delete last seed point if (this->m_PointSet->GetSize() > 0) { m_PointSet->RemovePointAtEnd(0); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - this->UpdatePreview(); } } } void mitk::PickingTool::ClearPicks() { this->ClearSeeds(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); this->UpdatePreview(); } bool mitk::PickingTool::HasPicks() const { return this->m_PointSet.IsNotNull() && this->m_PointSet->GetSize()>0; } void mitk::PickingTool::ClearSeeds() { if (this->m_PointSet.IsNotNull()) { // renew pointset this->m_PointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); this->m_PointSetNode->SetData(this->m_PointSet); } } template void DoITKRegionGrowing(const itk::Image* oldSegImage, mitk::Image* segmentation, const mitk::PointSet* seedPoints, unsigned int timeStep, const mitk::BaseGeometry* inputGeometry) { 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 for (auto pos = seedPoints->Begin(); pos != seedPoints->End(); ++pos) { IndexType seedIndex; inputGeometry->WorldToIndex(pos->Value(), seedIndex); regionGrower->AddSeed(seedIndex); } // perform region growing in desired segmented region regionGrower->SetInput(oldSegImage); regionGrower->SetLower(static_cast(1)); regionGrower->SetUpper(std::numeric_limits::max()); try { regionGrower->Update(); } catch (const itk::ExceptionObject &) { return; // can't work } catch (...) { return; } segmentation->SetVolume((void*)(regionGrower->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); } void mitk::PickingTool::DoUpdatePreview(const Image* /*inputAtTimeStep*/, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) { if (nullptr != oldSegAtTimeStep && nullptr != previewImage && m_PointSet.IsNotNull()) { AccessFixedDimensionByItk_n(oldSegAtTimeStep, DoITKRegionGrowing, 3, (previewImage, this->m_PointSet, timeStep, oldSegAtTimeStep->GetGeometry())); } -} \ No newline at end of file +} diff --git a/Modules/Segmentation/Interactions/mitkTool.h b/Modules/Segmentation/Interactions/mitkTool.h index c2b22ab3ad..c2f6af5a78 100644 --- a/Modules/Segmentation/Interactions/mitkTool.h +++ b/Modules/Segmentation/Interactions/mitkTool.h @@ -1,275 +1,275 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkTool_h_Included #define mitkTool_h_Included #include "itkObjectFactoryBase.h" #include "itkVersion.h" #include "mitkCommon.h" #include "mitkDataNode.h" #include "mitkEventStateMachine.h" #include "mitkInteractionEventObserver.h" #include "mitkLabelSetImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkToolEvents.h" #include "mitkToolFactoryMacro.h" #include #include #include #include #include #include #include "usServiceRegistration.h" 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 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() at the end of 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 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(const Image *original, const std::string &organName, const mitk::Color &color) const; DataNode::Pointer CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) const; /** Function used to check if a tool can handle the referenceData and (if specified) the working data. @pre referenceData must be a valid pointer @param referenceData Pointer to the data that should be checked as valid reference for the tool. @param workingData Pointer to the data that should be checked as valid working data for this tool. This parameter can be null if no working data is specified so far.*/ virtual bool CanHandle(const BaseData *referenceData, const BaseData *workingData) const; protected: friend class ToolManager; virtual void SetToolManager(ToolManager *); /** Returns the pointer to the tool manager of the tool. May be null.*/ ToolManager* GetToolManager() const; /** Returns the data storage provided by the toolmanager. May be null (e.g. if - Toolmanager is not set).*/ + ToolManager is not set).*/ mitk::DataStorage* GetDataStorage() const; void ConnectActionsAndFunctions() override; /** \brief Called when the tool gets activated. Derived tools should call their parents implementation at the beginning of the overriding function. */ virtual void Activated(); /** \brief Called when the tool gets deactivated. Derived tools should call their parents implementation at the end of the overriding function. */ virtual void Deactivated(); /** \brief Let subclasses change their event configuration. */ std::string m_EventConfig; Tool(const char *, const us::Module *interactorModule = nullptr); // purposely hidden ~Tool() override; void Notify(InteractionEvent *interactionEvent, bool isHandled) override; bool FilterEvents(InteractionEvent *, DataNode *) override; private: ToolManager* m_ToolManager; // 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; std::map m_DisplayInteractorConfigs; const us::Module *m_InteractorModule; }; } // namespace #endif