diff --git a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp new file mode 100644 index 0000000000..5451a67f34 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp @@ -0,0 +1,204 @@ +/*============================================================================ + +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. + +============================================================================*/ + +// MITK +#include "mitkAutoMLSegmentationWithPreviewTool.h" +#include "mitkImageAccessByItk.h" +#include "mitkToolManager.h" +#include +#include +#include +#include +#include +#include + +// ITK +#include +#include + +mitk::AutoMLSegmentationWithPreviewTool::AutoMLSegmentationWithPreviewTool() : AutoSegmentationWithPreviewTool(true) +{ +} + +void mitk::AutoMLSegmentationWithPreviewTool::SetSelectedLabels(const SelectedLabelVectorType& regions) +{ + if (m_SelectedLabels != regions) + { + m_SelectedLabels = regions; + //Note: we do not call this->Modified() on puprose. Reason: changing the + //selected regions should not force to run otsu filter in DoUpdatePreview due to changed MTime. + } +} + +const mitk::LabelSetImage* mitk::AutoMLSegmentationWithPreviewTool::GetMLPreview() const +{ + if (m_MLPreviewNode.IsNotNull()) + { + const auto mlPreviewImage = dynamic_cast(this->m_MLPreviewNode->GetData()); + return mlPreviewImage; + } + + return nullptr; +} + +mitk::AutoMLSegmentationWithPreviewTool::SelectedLabelVectorType mitk::AutoMLSegmentationWithPreviewTool::GetSelectedLabels() const +{ + return this->m_SelectedLabels; +} + +void mitk::AutoMLSegmentationWithPreviewTool::Activated() +{ + Superclass::Activated(); + + m_SelectedLabels = {}; + + m_MLPreviewNode = mitk::DataNode::New(); + m_MLPreviewNode->SetProperty("name", StringProperty::New(std::string(this->GetName()) + "ML preview")); + m_MLPreviewNode->SetProperty("helper object", BoolProperty::New(true)); + m_MLPreviewNode->SetVisibility(true); + m_MLPreviewNode->SetOpacity(1.0); + + m_ToolManager->GetDataStorage()->Add(m_MLPreviewNode); +} + +void mitk::AutoMLSegmentationWithPreviewTool::Deactivated() +{ + m_ToolManager->GetDataStorage()->Remove(m_MLPreviewNode); + m_MLPreviewNode = nullptr; + + Superclass::Deactivated(); +} + +void mitk::AutoMLSegmentationWithPreviewTool::UpdateCleanUp() +{ + if (m_MLPreviewNode.IsNotNull()) + m_MLPreviewNode->SetVisibility(m_SelectedLabels.empty()); + + if (nullptr != this->GetPreviewSegmentationNode()) + this->GetPreviewSegmentationNode()->SetVisibility(!m_SelectedLabels.empty()); + + if (m_SelectedLabels.empty()) + { + this->ResetPreviewNode(); + } +} + +void mitk::AutoMLSegmentationWithPreviewTool::DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) +{ + const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + + if (nullptr == m_MLPreviewNode->GetData() + || this->GetMTime() > m_MLPreviewNode->GetData()->GetMTime() + || this->m_LastMLTimeStep != timeStep //this covers the case where dynamic + //segmentations have to compute a preview + //for all time steps on confirmation + || this->GetLastTimePointOfUpdate() != timePoint //this ensures that static seg + //previews work with dynamic images + //with avoiding unnecessary other computations + ) + { + if (nullptr == inputAtTimeStep) + { + MITK_WARN << "Cannot run segementation. Currently selected input image is not set."; + return; + } + + this->m_LastMLTimeStep = timeStep; + + auto newMLPreview = ComputeMLPreview(inputAtTimeStep, timeStep); + + if (newMLPreview.IsNotNull()) + { + this->m_MLPreviewNode->SetData(newMLPreview); + this->m_MLPreviewNode->SetProperty("binary", mitk::BoolProperty::New(false)); + mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); + renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); + this->m_MLPreviewNode->SetProperty("Image Rendering.Mode", renderingMode); + mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); + mitk::LookupTableProperty::Pointer prop = mitk::LookupTableProperty::New(lut); + vtkSmartPointer lookupTable = vtkSmartPointer::New(); + lookupTable->SetHueRange(1.0, 0.0); + lookupTable->SetSaturationRange(1.0, 1.0); + lookupTable->SetValueRange(1.0, 1.0); + lookupTable->SetTableRange(-1.0, 1.0); + lookupTable->Build(); + lut->SetVtkLookupTable(lookupTable); + prop->SetLookupTable(lut); + this->m_MLPreviewNode->SetProperty("LookupTable", prop); + mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); + mitk::LevelWindow levelwindow; + levelwindow.SetRangeMinMax(0, newMLPreview->GetScalarValueMax()); + levWinProp->SetLevelWindow(levelwindow); + this->m_MLPreviewNode->SetProperty("levelwindow", levWinProp); + } + } + + if (!m_SelectedLabels.empty()) + { + const auto mlPreviewImage = this->GetMLPreview(); + if (nullptr != mlPreviewImage) + { + AccessByItk_n(mlPreviewImage, CalculateMergedSimplePreview, (previewImage, timeStep)); + } + } +} + +template +void mitk::AutoMLSegmentationWithPreviewTool::CalculateMergedSimplePreview(const itk::Image* itkImage, mitk::Image* segmentation, unsigned int timeStep) +{ + typedef itk::Image InputImageType; + typedef itk::Image OutputImageType; + + typedef itk::BinaryThresholdImageFilter FilterType; + + typename FilterType::Pointer filter = FilterType::New(); + + // InputImageType::Pointer itkImage; + typename OutputImageType::Pointer itkBinaryResultImage; + + filter->SetInput(itkImage); + filter->SetLowerThreshold(m_SelectedLabels[0]); + filter->SetUpperThreshold(m_SelectedLabels[0]); + filter->SetInsideValue(1); + filter->SetOutsideValue(0); + filter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + filter->Update(); + itkBinaryResultImage = filter->GetOutput(); + itkBinaryResultImage->DisconnectPipeline(); + + // if more than one region id is used compute the union of all given binary regions + for (const auto labelID : m_SelectedLabels) + { + if (labelID != m_SelectedLabels[0]) + { + filter->SetLowerThreshold(labelID); + filter->SetUpperThreshold(labelID); + filter->SetInsideValue(1); + filter->SetOutsideValue(0); + filter->Update(); + + typename OutputImageType::Pointer tempImage = filter->GetOutput(); + + typename itk::OrImageFilter::Pointer orFilter = + itk::OrImageFilter::New(); + orFilter->SetInput1(tempImage); + orFilter->SetInput2(itkBinaryResultImage); + orFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + + orFilter->UpdateLargestPossibleRegion(); + itkBinaryResultImage = orFilter->GetOutput(); + } + } + //---------------------------------------------------------------------------------------------------- + + segmentation->SetVolume((void*)(itkBinaryResultImage->GetPixelContainer()->GetBufferPointer()), timeStep); +} diff --git a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.h b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.h new file mode 100644 index 0000000000..94163df1ad --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.h @@ -0,0 +1,82 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ +#ifndef MITK_AUTO_ML_SEGMENTATION_WITH_PREVIEW_TOOL_H +#define MITK_AUTO_ML_SEGMENTATION_WITH_PREVIEW_TOOL_H + +#include "mitkAutoSegmentationWithPreviewTool.h" +#include "mitkDataNode.h" +#include "mitkLabelSetImage.h" + +#include + +namespace mitk +{ + /** + \brief Base class for any auto segmentation tool that provides a preview of the new segmentation and generates + segmentations with multiple labels. + + This tool class implements the basic logic to handle previews of multi label segmentations and + to allow to pick arbitrary labels as selected and merge them to a single segmentation to store this + segmentation as confirmed segmentation. + + \ingroup ToolManagerEtAl + \sa mitk::Tool + \sa QmitkInteractiveSegmentation + */ + class MITKSEGMENTATION_EXPORT AutoMLSegmentationWithPreviewTool : public AutoSegmentationWithPreviewTool + { + public: + mitkClassMacro(AutoMLSegmentationWithPreviewTool, AutoSegmentationWithPreviewTool); + + void Activated() override; + void Deactivated() override; + + using SelectedLabelVectorType = std::vector; + void SetSelectedLabels(const SelectedLabelVectorType& regions); + SelectedLabelVectorType GetSelectedLabels() const; + + const LabelSetImage* GetMLPreview() const; + + protected: + AutoMLSegmentationWithPreviewTool(); + ~AutoMLSegmentationWithPreviewTool() = default; + + void UpdateCleanUp() override; + void DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) override; + + /** Function to generate the new multi lable preview for a given time step input image. + * The function must be implemented by derived tools. + * This function is called by DoUpdatePreview if needed. + * Reasons are: + * - ML preview does not exist + * - Modify time of tools is newer then of ML preview + * - ML preview was not generated for the current selected timestep of input image or for the current selected timepoint.*/ + virtual LabelSetImage::Pointer ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType timeStep) = 0; + + private: + /** Function to generate a simple (single lable) preview by merging all labels of the ML preview that are selected and + * copies that single label preview to the passed previewImage. + * This function is called by DoUpdatePreview if needed. + * @param mlPreviewImage Multi label preview that is the source. + * @param previewImage Pointer to the single label preview image that should receive the merged selected labels. + * @timeStep Time step of the previewImage that should be filled.*/ + template + void CalculateMergedSimplePreview(const itk::Image* mlImage, mitk::Image* segmentation, unsigned int timeStep); + + SelectedLabelVectorType m_SelectedLabels = {}; + + // holds the multilabel result as a preview image + mitk::DataNode::Pointer m_MLPreviewNode; + TimeStepType m_LastMLTimeStep = 0; + }; +} +#endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp index 8e9b4291f7..e8554c2228 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp @@ -1,474 +1,478 @@ /*============================================================================ 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" mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews): 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; try { if (DataStorage *storage = m_ToolManager->GetDataStorage()) { storage->Remove(m_PreviewSegmentationNode); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (...) { // don't care } - m_PreviewSegmentationNode->SetData(nullptr); + 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); } 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); + this->UpdatePrepare(); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { - this->CurrentlyBusy.Send(true); 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); } } else { auto feedBackImage3D = this->GetImageByTimePoint(inputImage, timePoint); auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->DoUpdatePreview(feedBackImage3D, 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); CurrentlyBusy.Send(false); throw; } this->UpdateCleanUp(); m_LastTimePointOfUpdate = timePoint; m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); } 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/mitkOtsuTool3D.cpp b/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp index 9a5f4cc388..ec02f6a346 100644 --- a/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp +++ b/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp @@ -1,253 +1,86 @@ /*============================================================================ 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. ============================================================================*/ // MITK #include "mitkOtsuTool3D.h" -#include "mitkImageAccessByItk.h" -#include "mitkLabelSetImage.h" #include "mitkOtsuSegmentationFilter.h" -#include "mitkRenderingManager.h" -#include "mitkToolManager.h" -#include -#include -#include -#include -#include -#include - -// ITK -#include -#include -#include // us #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, OtsuTool3D, "Otsu Segmentation"); } -mitk::OtsuTool3D::OtsuTool3D() : AutoSegmentationWithPreviewTool(true) -{ -} - -mitk::OtsuTool3D::~OtsuTool3D() -{ -} - -void mitk::OtsuTool3D::SetSelectedRegions(const SelectedRegionVectorType& regions) -{ - if (m_SelectedRegions != regions) - { - m_SelectedRegions = regions; - //Note: we do not call this->Modified() on puprose. Reason: changing the - //selected regions should not force to run otsu filter in DoUpdatePreview due to changed MTime. - } -} - -mitk::OtsuTool3D::SelectedRegionVectorType mitk::OtsuTool3D::GetSelectedRegions() const -{ - return this->m_SelectedRegions; -} - void mitk::OtsuTool3D::Activated() { Superclass::Activated(); - m_SelectedRegions = {}; m_NumberOfBins = 128; m_NumberOfRegions = 2; m_UseValley = false; - - m_OtsuResultNode = mitk::DataNode::New(); - m_OtsuResultNode->SetName("Otsu_Preview"); - // m_MultiLabelResultNode->SetBoolProperty("helper object", true); - m_OtsuResultNode->SetVisibility(true); - m_OtsuResultNode->SetOpacity(1.0); - - m_ToolManager->GetDataStorage()->Add(m_OtsuResultNode); -} - -void mitk::OtsuTool3D::Deactivated() -{ - m_ToolManager->GetDataStorage()->Remove(m_OtsuResultNode); - m_OtsuResultNode = nullptr; - - Superclass::Deactivated(); } const char **mitk::OtsuTool3D::GetXPM() const { return nullptr; } us::ModuleResource mitk::OtsuTool3D::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Otsu_48x48.png"); return resource; } const char* mitk::OtsuTool3D::GetName() const { return "Otsu"; } -void mitk::OtsuTool3D::UpdateCleanUp() -{ - if (m_OtsuResultNode.IsNotNull()) - m_OtsuResultNode->SetVisibility(m_SelectedRegions.empty()); - - if (nullptr != this->GetPreviewSegmentationNode()) - this->GetPreviewSegmentationNode()->SetVisibility(!m_SelectedRegions.empty()); - - if (m_SelectedRegions.empty()) - { - this->ResetPreviewNode(); - } -} - -void mitk::OtsuTool3D::DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) +mitk::LabelSetImage::Pointer mitk::OtsuTool3D::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/) { int numberOfThresholds = m_NumberOfRegions - 1; - const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - mitk::LabelSetImage::Pointer otsuResultImage = dynamic_cast(this->m_OtsuResultNode->GetData()); - - if (nullptr == m_OtsuResultNode->GetData() - || this->GetMTime() > m_OtsuResultNode->GetData()->GetMTime() - || this->m_LastOtsuTimeStep != timeStep //this covers the case where dynamic - //segmentations have to compute a preview - //for all time steps on confirmation - || this->GetLastTimePointOfUpdate() != timePoint //this ensures that static seg - //previews work with dynamic images - //with avoiding unnecessary other otsu computations - ) - { - if (nullptr == inputAtTimeStep) - { - MITK_WARN << "Cannot run segementation. Currently selected input image is not set."; - return; - } - - this->m_LastOtsuTimeStep = timeStep; - - mitk::OtsuSegmentationFilter::Pointer otsuFilter = mitk::OtsuSegmentationFilter::New(); - otsuFilter->SetNumberOfThresholds(numberOfThresholds); - otsuFilter->SetValleyEmphasis(m_UseValley); - otsuFilter->SetNumberOfBins(m_NumberOfBins); - otsuFilter->SetInput(inputAtTimeStep); - otsuFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - - try - { - otsuFilter->Update(); - } - catch (...) - { - mitkThrow() << "itkOtsuFilter error (image dimension must be in {2, 3} and image must not be RGB)"; - } - - otsuResultImage = mitk::LabelSetImage::New(); - otsuResultImage->InitializeByLabeledImage(otsuFilter->GetOutput()); - this->m_OtsuResultNode->SetData(otsuResultImage); - this->m_OtsuResultNode->SetProperty("binary", mitk::BoolProperty::New(false)); - mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); - renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); - this->m_OtsuResultNode->SetProperty("Image Rendering.Mode", renderingMode); - mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); - mitk::LookupTableProperty::Pointer prop = mitk::LookupTableProperty::New(lut); - vtkSmartPointer lookupTable = vtkSmartPointer::New(); - lookupTable->SetHueRange(1.0, 0.0); - lookupTable->SetSaturationRange(1.0, 1.0); - lookupTable->SetValueRange(1.0, 1.0); - lookupTable->SetTableRange(-1.0, 1.0); - lookupTable->Build(); - lut->SetVtkLookupTable(lookupTable); - prop->SetLookupTable(lut); - this->m_OtsuResultNode->SetProperty("LookupTable", prop); - mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); - mitk::LevelWindow levelwindow; - levelwindow.SetRangeMinMax(0, numberOfThresholds + 1); - levWinProp->SetLevelWindow(levelwindow); - this->m_OtsuResultNode->SetProperty("levelwindow", levWinProp); - } + mitk::OtsuSegmentationFilter::Pointer otsuFilter = mitk::OtsuSegmentationFilter::New(); + otsuFilter->SetNumberOfThresholds(numberOfThresholds); + otsuFilter->SetValleyEmphasis(m_UseValley); + otsuFilter->SetNumberOfBins(m_NumberOfBins); + otsuFilter->SetInput(inputAtTimeStep); + otsuFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - if (!m_SelectedRegions.empty()) + try { - AccessByItk_n(otsuResultImage, CalculatePreview, (previewImage, timeStep)); + otsuFilter->Update(); } -} - -template -void mitk::OtsuTool3D::CalculatePreview(itk::Image *itkImage, mitk::Image* segmentation, unsigned int timeStep) -{ - typedef itk::Image InputImageType; - typedef itk::Image OutputImageType; - - typedef itk::BinaryThresholdImageFilter FilterType; - - typename FilterType::Pointer filter = FilterType::New(); - - // InputImageType::Pointer itkImage; - typename OutputImageType::Pointer itkBinaryResultImage; - - filter->SetInput(itkImage); - filter->SetLowerThreshold(m_SelectedRegions[0]); - filter->SetUpperThreshold(m_SelectedRegions[0]); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - filter->Update(); - itkBinaryResultImage = filter->GetOutput(); - itkBinaryResultImage->DisconnectPipeline(); - - // if more than one region id is used compute the union of all given binary regions - for (const auto regionID : m_SelectedRegions) + catch (...) { - if (regionID != m_SelectedRegions[0]) - { - filter->SetLowerThreshold(regionID); - filter->SetUpperThreshold(regionID); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->Update(); - - typename OutputImageType::Pointer tempImage = filter->GetOutput(); - - typename itk::OrImageFilter::Pointer orFilter = - itk::OrImageFilter::New(); - orFilter->SetInput1(tempImage); - orFilter->SetInput2(itkBinaryResultImage); - orFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - - orFilter->UpdateLargestPossibleRegion(); - itkBinaryResultImage = orFilter->GetOutput(); - } + mitkThrow() << "itkOtsuFilter error (image dimension must be in {2, 3} and image must not be RGB)"; } - //---------------------------------------------------------------------------------------------------- - segmentation->SetVolume((void*)(itkBinaryResultImage->GetPixelContainer()->GetBufferPointer()), timeStep); + auto otsuResultImage = mitk::LabelSetImage::New(); + otsuResultImage->InitializeByLabeledImage(otsuFilter->GetOutput()); + return otsuResultImage; } unsigned int mitk::OtsuTool3D::GetMaxNumberOfBins() const { const auto min = this->GetReferenceData()->GetStatistics()->GetScalarValueMin(); const auto max = this->GetReferenceData()->GetStatistics()->GetScalarValueMaxNoRecompute(); return static_cast(max - min) + 1; } diff --git a/Modules/Segmentation/Interactions/mitkOtsuTool3D.h b/Modules/Segmentation/Interactions/mitkOtsuTool3D.h index 7761ec60dd..3703b237a6 100644 --- a/Modules/Segmentation/Interactions/mitkOtsuTool3D.h +++ b/Modules/Segmentation/Interactions/mitkOtsuTool3D.h @@ -1,80 +1,64 @@ /*============================================================================ 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 MITKOTSUTOOL3D_H #define MITKOTSUTOOL3D_H -#include "itkImage.h" -#include "mitkAutoSegmentationWithPreviewTool.h" -#include "mitkDataNode.h" +#include "mitkAutoMLSegmentationWithPreviewTool.h" #include namespace us { class ModuleResource; } namespace mitk { class Image; - class MITKSEGMENTATION_EXPORT OtsuTool3D : public AutoSegmentationWithPreviewTool + class MITKSEGMENTATION_EXPORT OtsuTool3D : public AutoMLSegmentationWithPreviewTool { public: - mitkClassMacro(OtsuTool3D, AutoSegmentationWithPreviewTool); + mitkClassMacro(OtsuTool3D, AutoMLSegmentationWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char *GetName() const override; const char **GetXPM() const override; us::ModuleResource GetIconResource() const override; void Activated() override; - void Deactivated() override; itkSetMacro(NumberOfBins, unsigned int); itkGetConstMacro(NumberOfBins, unsigned int); itkSetMacro(NumberOfRegions, unsigned int); itkGetConstMacro(NumberOfRegions, unsigned int); itkSetMacro(UseValley, bool); itkGetConstMacro(UseValley, bool); itkBooleanMacro(UseValley); - using SelectedRegionVectorType = std::vector; - void SetSelectedRegions(const SelectedRegionVectorType& regions); - SelectedRegionVectorType GetSelectedRegions() const; - /**Returns the number of max bins based on the current input image.*/ unsigned int GetMaxNumberOfBins() const; protected: - OtsuTool3D(); - ~OtsuTool3D() override; - - void UpdateCleanUp() override; - void DoUpdatePreview(const Image* inputAtTimeStep, Image* previewImage, TimeStepType timeStep) override; + OtsuTool3D() = default; + ~OtsuTool3D() = default; - template - void CalculatePreview(itk::Image *itkImage, mitk::Image* segmentation, unsigned int timeStep); + LabelSetImage::Pointer ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType timeStep) override; unsigned int m_NumberOfBins = 128; unsigned int m_NumberOfRegions = 2; bool m_UseValley = false; - SelectedRegionVectorType m_SelectedRegions = {}; - - // holds the multilabel result as a preview image - mitk::DataNode::Pointer m_OtsuResultNode; - TimeStepType m_LastOtsuTimeStep = 0; }; // class } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp index 47e29e4da4..b533bbe242 100644 --- a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp +++ b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp @@ -1,188 +1,163 @@ /*============================================================================ 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 "mitkWatershedTool.h" #include "mitkIOUtil.h" #include "mitkITKImageImport.h" #include "mitkImage.h" #include "mitkLabelSetImage.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkLevelWindowManager.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkProgressBar.h" #include "mitkRenderingManager.h" #include "mitkRenderingModeProperty.h" #include "mitkToolCommand.h" #include "mitkToolManager.h" #include #include #include #include #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, WatershedTool, "Watershed tool"); } -mitk::WatershedTool::WatershedTool() : m_Threshold(0.0), m_Level(0.0) -{ -} - -mitk::WatershedTool::~WatershedTool() -{ -} void mitk::WatershedTool::Activated() { Superclass::Activated(); -} -void mitk::WatershedTool::Deactivated() -{ - Superclass::Deactivated(); + m_Level = 0.0; + m_Threshold = 0.0; + + m_MagFilter = nullptr; + m_WatershedFilter = nullptr; + m_LastFilterInput = nullptr; } us::ModuleResource mitk::WatershedTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Watershed_48x48.png"); return resource; } const char **mitk::WatershedTool::GetXPM() const { return nullptr; } const char *mitk::WatershedTool::GetName() const { return "Watershed"; } -void mitk::WatershedTool::DoIt() +mitk::LabelSetImage::Pointer mitk::WatershedTool::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/) { - // get image from tool manager - mitk::DataNode::Pointer referenceData = m_ToolManager->GetReferenceData(0); - mitk::Image::ConstPointer input = dynamic_cast(referenceData->GetData()); - if (input.IsNull()) - return; - - const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - input = GetImageByTimePoint(input, timePoint); - - if (nullptr == input) - { - MITK_WARN << "Cannot run segementation. Currently selected timepoint is not in the time bounds of the selected reference image. Time point: " << timePoint; - return; - } - - mitk::Image::Pointer output; + mitk::LabelSetImage::Pointer labelSetOutput; try { + mitk::Image::Pointer output; + bool inputChanged = inputAtTimeStep != m_LastFilterInput; // create and run itk filter pipeline - AccessByItk_1(input.GetPointer(), ITKWatershed, output); + AccessByItk_2(inputAtTimeStep, ITKWatershed, output, inputChanged); - mitk::LabelSetImage::Pointer labelSetOutput = mitk::LabelSetImage::New(); + labelSetOutput = mitk::LabelSetImage::New(); labelSetOutput->InitializeByLabeledImage(output); - - // create a new datanode for output - mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); - dataNode->SetData(labelSetOutput); - - // set name of data node - std::string name = referenceData->GetName() + "_Watershed"; - dataNode->SetName(name); - - // look, if there is already a node with this name - mitk::DataStorage::SetOfObjects::ConstPointer children = - m_ToolManager->GetDataStorage()->GetDerivations(referenceData); - mitk::DataStorage::SetOfObjects::ConstIterator currentNode = children->Begin(); - mitk::DataNode::Pointer removeNode; - while (currentNode != children->End()) - { - if (dataNode->GetName().compare(currentNode->Value()->GetName()) == 0) - { - removeNode = currentNode->Value(); - } - currentNode++; - } - // remove node with same name - if (removeNode.IsNotNull()) - m_ToolManager->GetDataStorage()->Remove(removeNode); - - // add output to the data storage - m_ToolManager->GetDataStorage()->Add(dataNode, referenceData); } - catch (itk::ExceptionObject &e) + catch (itk::ExceptionObject & e) { + //force reset of filters as they might be in an invalid state now. + m_MagFilter = nullptr; + m_WatershedFilter = nullptr; + m_LastFilterInput = nullptr; + MITK_ERROR << "Watershed Filter Error: " << e.GetDescription(); } + + m_LastFilterInput = inputAtTimeStep; - RenderingManager::GetInstance()->RequestUpdateAll(); + return labelSetOutput; } template -void mitk::WatershedTool::ITKWatershed(const itk::Image *originalImage, - mitk::Image::Pointer &segmentation) +void mitk::WatershedTool::ITKWatershed(const itk::Image* originalImage, + mitk::Image::Pointer& segmentation, bool inputChanged) { typedef itk::WatershedImageFilter> WatershedFilter; typedef itk::GradientMagnitudeRecursiveGaussianImageFilter, - itk::Image> + itk::Image> MagnitudeFilter; + // We create the filter pipeline only once (if needed) and not everytime we + // generate the ml image preview. + // Reason: If only the levels are changed the update of the pipe line is very + // fast and we want to profit from this feature. + // at first add a gradient magnitude filter - typename MagnitudeFilter::Pointer magnitude = MagnitudeFilter::New(); - magnitude->SetInput(originalImage); - magnitude->SetSigma(1.0); + typename MagnitudeFilter::Pointer magnitude = dynamic_cast(m_MagFilter.GetPointer()); + if (magnitude.IsNull()) + { + magnitude = MagnitudeFilter::New(); + magnitude->SetSigma(1.0); + magnitude->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + m_MagFilter = magnitude.GetPointer(); + } - // use the progress bar - mitk::ToolCommand::Pointer command = mitk::ToolCommand::New(); - command->AddStepsToDo(60); + if (inputChanged) + { + magnitude->SetInput(originalImage); + } // then add the watershed filter to the pipeline - typename WatershedFilter::Pointer watershed = WatershedFilter::New(); - watershed->SetInput(magnitude->GetOutput()); + typename WatershedFilter::Pointer watershed = dynamic_cast(m_WatershedFilter.GetPointer()); + if (watershed.IsNull()) + { + watershed = WatershedFilter::New(); + watershed->SetInput(magnitude->GetOutput()); + watershed->AddObserver(itk::ProgressEvent(), m_ProgressCommand); + m_WatershedFilter = watershed.GetPointer(); + } + watershed->SetThreshold(m_Threshold); watershed->SetLevel(m_Level); - watershed->AddObserver(itk::ProgressEvent(), command); watershed->Update(); // then make sure, that the output has the desired pixel type typedef itk::CastImageFilter> + itk::Image> CastFilter; typename CastFilter::Pointer cast = CastFilter::New(); cast->SetInput(watershed->GetOutput()); // start the whole pipeline cast->Update(); - // reset the progress bar by setting progress - command->SetProgress(10); - // since we obtain a new image from our pipeline, we have to make sure, that our mitk::Image::Pointer // is responsible for the memory management of the output image segmentation = mitk::GrabItkImageMemory(cast->GetOutput()); } diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.h b/Modules/Segmentation/Interactions/mitkWatershedTool.h index f50734141b..a4bea005ec 100644 --- a/Modules/Segmentation/Interactions/mitkWatershedTool.h +++ b/Modules/Segmentation/Interactions/mitkWatershedTool.h @@ -1,88 +1,84 @@ /*============================================================================ 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 mitkWatershedTool_h_Included #define mitkWatershedTool_h_Included -#include "mitkAutoSegmentationTool.h" +#include "mitkAutoMLSegmentationWithPreviewTool.h" #include "mitkCommon.h" #include -#include namespace us { class ModuleResource; } namespace mitk { - class Image; - /** \brief Simple watershed segmentation tool. \ingroup Interaction \ingroup ToolManagerEtAl Wraps ITK Watershed Filter into tool concept of MITK. For more information look into ITK documentation. \warning Only to be instantiated by mitk::ToolManager. - - $Darth Vader$ */ - class MITKSEGMENTATION_EXPORT WatershedTool : public AutoSegmentationTool + class MITKSEGMENTATION_EXPORT WatershedTool : public AutoMLSegmentationWithPreviewTool { public: - mitkClassMacro(WatershedTool, AutoSegmentationTool); + mitkClassMacro(WatershedTool, AutoMLSegmentationWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - void SetThreshold(double t) - { - m_Threshold = t; - } + const char** GetXPM() const override; + const char* GetName() const override; + us::ModuleResource GetIconResource() const override; + + void Activated() override; + + itkSetMacro(Threshold, double); + itkGetConstMacro(Threshold, double); + + itkSetMacro(Level, double); + itkGetConstMacro(Level, double); + + protected: + WatershedTool() = default; + ~WatershedTool() = default; + + LabelSetImage::Pointer ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType timeStep) override; - void SetLevel(double l) { m_Level = l; } - /** \brief Grabs the tool reference data and creates an ITK pipeline consisting of a GradientMagnitude - * image filter followed by a Watershed image filter. The output of the filter pipeline is then added - * to the data storage. */ - void DoIt(); + /** \brief Threshold parameter of the ITK Watershed Image Filter. See ITK Documentation for more information. */ + double m_Threshold = 0.0; + /** \brief Threshold parameter of the ITK Watershed Image Filter. See ITK Documentation for more information. */ + double m_Level = 0.0; +private: /** \brief Creates and runs an ITK filter pipeline consisting of the filters: GradientMagnitude-, Watershed- and * CastImageFilter. * * \param originalImage The input image, which is delivered by the AccessByItk macro. * \param segmentation A pointer to the output image, which will point to the pipeline output after execution. */ template - void ITKWatershed(const itk::Image *originalImage, itk::SmartPointer &segmentation); - - const char **GetXPM() const override; - const char *GetName() const override; - us::ModuleResource GetIconResource() const override; - - protected: - WatershedTool(); // purposely hidden - ~WatershedTool() override; - - void Activated() override; - void Deactivated() override; + void ITKWatershed(const itk::Image* originalImage, itk::SmartPointer& segmentation, bool inputChanged); - /** \brief Threshold parameter of the ITK Watershed Image Filter. See ITK Documentation for more information. */ - double m_Threshold; - /** \brief Threshold parameter of the ITK Watershed Image Filter. See ITK Documentation for more information. */ - double m_Level; + itk::ProcessObject::Pointer m_MagFilter; + itk::ProcessObject::Pointer m_WatershedFilter; + mitk::Image::ConstPointer m_LastFilterInput; }; } // namespace #endif diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 1deb993fd5..3bce31c9b5 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,117 +1,118 @@ 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/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 ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp index 031b353dab..9e925b66ba 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp @@ -1,214 +1,194 @@ /*============================================================================ 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 "QmitkOtsuTool3DGUI.h" -#include "QmitkConfirmSegmentationDialog.h" #include -#include -#include -#include -#include -#include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkOtsuTool3DGUI, "") QmitkOtsuTool3DGUI::QmitkOtsuTool3DGUI() : QmitkToolGUI(), m_NumberOfRegions(0) { m_Controls.setupUi(this); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnSpinboxValueAccept())); - connect(m_Controls.m_selectionListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(OnRegionSelectionChanged())); + connect(m_Controls.m_selectionListWidget, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkOtsuTool3DGUI::OnRegionSelectionChanged); connect(m_Controls.m_Spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnRegionSpinboxChanged(int))); connect(m_Controls.m_ConfSegButton, SIGNAL(clicked()), this, SLOT(OnSegmentationRegionAccept())); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); connect(m_Controls.advancedSettingsButton, SIGNAL(toggled(bool)), this, SLOT(OnAdvancedSettingsButtonToggled(bool))); this->OnAdvancedSettingsButtonToggled(false); } QmitkOtsuTool3DGUI::~QmitkOtsuTool3DGUI() { if (m_OtsuTool3DTool.IsNotNull()) { m_OtsuTool3DTool->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkOtsuTool3DGUI::BusyStateChanged); } } void QmitkOtsuTool3DGUI::OnRegionSpinboxChanged(int numberOfRegions) { // we have to change to minimum number of histogram bins accordingly int curBinValue = m_Controls.m_BinsSpinBox->value(); if (curBinValue < numberOfRegions) m_Controls.m_BinsSpinBox->setValue(numberOfRegions); } -void QmitkOtsuTool3DGUI::OnRegionSelectionChanged() +void QmitkOtsuTool3DGUI::OnRegionSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels) { - m_SelectedItems = m_Controls.m_selectionListWidget->selectedItems(); - if (m_OtsuTool3DTool.IsNotNull()) { - // update preview of region - QList::Iterator it; - std::vector regionIDs; - for (it = m_SelectedItems.begin(); it != m_SelectedItems.end(); ++it) - regionIDs.push_back((*it)->text().toInt()); + mitk::AutoMLSegmentationWithPreviewTool::SelectedLabelVectorType labelIDs; + for (const auto& label : selectedLabels) + { + labelIDs.push_back(label->GetValue()); + } - m_OtsuTool3DTool->SetSelectedRegions(regionIDs); + m_OtsuTool3DTool->SetSelectedLabels(labelIDs); m_OtsuTool3DTool->UpdatePreview(); - m_Controls.m_ConfSegButton->setEnabled(!regionIDs.empty()); + m_Controls.m_ConfSegButton->setEnabled(!labelIDs.empty()); } } void QmitkOtsuTool3DGUI::OnAdvancedSettingsButtonToggled(bool toggled) { m_Controls.m_ValleyCheckbox->setVisible(toggled); m_Controls.binLabel->setVisible(toggled); m_Controls.m_BinsSpinBox->setVisible(toggled); if (toggled) { int max = m_OtsuTool3DTool->GetMaxNumberOfBins(); if (max >= m_Controls.m_BinsSpinBox->minimum()) { m_Controls.m_BinsSpinBox->setMaximum(max); } } } void QmitkOtsuTool3DGUI::OnNewToolAssociated(mitk::Tool *tool) { if (m_OtsuTool3DTool.IsNotNull()) { m_OtsuTool3DTool->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkOtsuTool3DGUI::BusyStateChanged); } m_OtsuTool3DTool = dynamic_cast(tool); if (m_OtsuTool3DTool.IsNotNull()) { m_OtsuTool3DTool->CurrentlyBusy += mitk::MessageDelegate1(this, &QmitkOtsuTool3DGUI::BusyStateChanged); m_OtsuTool3DTool->SetOverwriteExistingSegmentation(true); m_OtsuTool3DTool->IsTimePointChangeAwareOff(); m_Controls.m_CheckProcessAll->setVisible(m_OtsuTool3DTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps() > 1); } } void QmitkOtsuTool3DGUI::OnSegmentationRegionAccept() { - QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_OtsuTool3DTool->GetCurrentSegmentationName()); if (m_OtsuTool3DTool.IsNotNull()) { if (this->m_Controls.m_CheckCreateNew->isChecked()) { m_OtsuTool3DTool->SetOverwriteExistingSegmentation(false); } else { m_OtsuTool3DTool->SetOverwriteExistingSegmentation(true); } m_OtsuTool3DTool->SetCreateAllTimeSteps(this->m_Controls.m_CheckProcessAll->isChecked()); this->m_Controls.m_ConfSegButton->setEnabled(false); m_OtsuTool3DTool->ConfirmSegmentation(); } } void QmitkOtsuTool3DGUI::OnSpinboxValueAccept() { if (m_NumberOfRegions == m_Controls.m_Spinbox->value() && m_UseValleyEmphasis == m_Controls.m_ValleyCheckbox->isChecked() && m_NumberOfBins == m_Controls.m_BinsSpinBox->value()) return; if (m_OtsuTool3DTool.IsNotNull()) { try { int proceed; QMessageBox *messageBox = new QMessageBox(QMessageBox::Question, nullptr, "The otsu segmentation computation may take several minutes depending " "on the number of Regions you selected. Proceed anyway?", QMessageBox::Ok | QMessageBox::Cancel); if (m_Controls.m_Spinbox->value() >= 5) { proceed = messageBox->exec(); if (proceed != QMessageBox::Ok) return; } m_NumberOfRegions = m_Controls.m_Spinbox->value(); m_UseValleyEmphasis = m_Controls.m_ValleyCheckbox->isChecked(); m_NumberOfBins = m_Controls.m_BinsSpinBox->value(); m_OtsuTool3DTool->SetNumberOfRegions(m_NumberOfRegions); m_OtsuTool3DTool->SetUseValley(m_UseValleyEmphasis); m_OtsuTool3DTool->SetNumberOfBins(m_NumberOfBins); m_OtsuTool3DTool->UpdatePreview(); } catch (...) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(QMessageBox::Critical, nullptr, "itkOtsuFilter error: image dimension must be in {2, 3} and no RGB images can be handled."); messageBox->exec(); delete messageBox; return; } - // insert regions into widget - QString itemName; - QListWidgetItem *item; - m_Controls.m_selectionListWidget->clear(); - for (int i = 0; i < m_Controls.m_Spinbox->value(); ++i) - { - itemName = QString::number(i); - item = new QListWidgetItem(itemName); - m_Controls.m_selectionListWidget->addItem(item); - } - // deactivate 'confirm segmentation'-button - m_Controls.m_ConfSegButton->setEnabled(false); + m_Controls.m_selectionListWidget->SetLabelSetImage(m_OtsuTool3DTool->GetMLPreview()); m_OtsuTool3DTool->IsTimePointChangeAwareOn(); } } void QmitkOtsuTool3DGUI::BusyStateChanged(bool value) { if (value) { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); } else { QApplication::restoreOverrideCursor(); } m_Controls.m_ValleyCheckbox->setEnabled(!value); m_Controls.binLabel->setEnabled(!value); m_Controls.m_BinsSpinBox->setEnabled(!value); - m_Controls.m_ConfSegButton->setEnabled(!m_OtsuTool3DTool->GetSelectedRegions().empty() && !value); + m_Controls.m_ConfSegButton->setEnabled(!m_OtsuTool3DTool->GetSelectedLabels().empty() && !value); m_Controls.m_CheckProcessAll->setEnabled(!value); m_Controls.m_CheckCreateNew->setEnabled(!value); m_Controls.previewButton->setEnabled(!value); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h index ce2ad862fe..e7a3762c43 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h @@ -1,83 +1,75 @@ /*============================================================================ 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 QmitkOtsuTool3DGUI_h_Included #define QmitkOtsuTool3DGUI_h_Included #include "QmitkToolGUI.h" #include "mitkOtsuTool3D.h" + #include "ui_QmitkOtsuToolWidgetControls.h" + #include -#include #include -class QSpinBox; -class QLabel; - /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::. \sa mitk:: This GUI shows ... Last contributor: $Author$ */ class MITKSEGMENTATIONUI_EXPORT QmitkOtsuTool3DGUI : public QmitkToolGUI { Q_OBJECT public: mitkClassMacro(QmitkOtsuTool3DGUI, QmitkToolGUI); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - signals : +protected slots : - public slots : - - protected slots : - - void OnNewToolAssociated(mitk::Tool *); + void OnNewToolAssociated(mitk::Tool *); void OnSpinboxValueAccept(); void OnSegmentationRegionAccept(); - void OnRegionSelectionChanged(); + void OnRegionSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels); void OnRegionSpinboxChanged(int); private slots: void OnAdvancedSettingsButtonToggled(bool toggled); protected: QmitkOtsuTool3DGUI(); ~QmitkOtsuTool3DGUI() override; void BusyStateChanged(bool value) override; mitk::OtsuTool3D::Pointer m_OtsuTool3DTool; Ui_QmitkOtsuToolWidgetControls m_Controls; int m_NumberOfRegions; bool m_UseValleyEmphasis; int m_NumberOfBins; - - QList m_SelectedItems; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui b/Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui index 27e1d87ff8..d01d26178d 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui @@ -1,234 +1,253 @@ QmitkOtsuToolWidgetControls 0 0 - 192 - 300 + 699 + 352 0 0 100 0 100000 100000 QmitkOtsuToolWidget - - Move to adjust the segmentation - QLayout::SetNoConstraint - + 0 0 Number of Regions: - + 0 0 40 16777215 2 32 + + + + Qt::Horizontal + + + + 40 + 20 + + + + 0 0 0 32 Advanced settings Qt::ToolButtonTextBesideIcon true - + + + + + 2 + + + 4096 + + + 128 + + + Use Valley Emphasis Number of Histogram Bins: - - - - 2 - - - 4096 + + + + Qt::Horizontal - - 128 + + + 40 + 20 + - + - + 0 0 10000000 - 100 + 10000000 - - 0 - - - QAbstractItemView::MultiSelection - - - QListView::Adjust - - + 0 0 100000 16777215 Preview false - + 0 0 100000 16777215 Confirm Segmentation Process/overwrite all time steps of the dynamic segmentation and not just the currently visible time step. Process all time steps Add the confirmed segmentation as a new segmentation instead of overwriting the currently selected. Create as new segmentation ctkExpandButton QToolButton
ctkExpandButton.h
+ + QmitkSimpleLabelSetListWidget + QWidget +
QmitkSimpleLabelSetListWidget.h
+
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp new file mode 100644 index 0000000000..8d6f3215f9 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp @@ -0,0 +1,182 @@ +/*============================================================================ + +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 "QmitkSimpleLabelSetListWidget.h" + +#include "mitkMessage.h" + +#include + +QmitkSimpleLabelSetListWidget::QmitkSimpleLabelSetListWidget(QWidget* parent) : QWidget(parent), m_LabelList(nullptr) +{ + QGridLayout* layout = new QGridLayout(this); + this->setContentsMargins(0, 0, 0, 0); + + m_LabelList = new QListWidget(this); + m_LabelList->setSelectionMode(QAbstractItemView::MultiSelection); + m_LabelList->setResizeMode(QListView::Adjust); + m_LabelList->setAutoScrollMargin(0); + layout->addWidget(m_LabelList); + + connect(m_LabelList, SIGNAL(itemSelectionChanged()), this, SLOT(OnLabelSelectionChanged())); +} + +QmitkSimpleLabelSetListWidget::~QmitkSimpleLabelSetListWidget() +{ + if (m_LabelSetImage.IsNotNull()) + { + m_LabelSetImage->BeforeChangeLayerEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection); + m_LabelSetImage->AfterChangeLayerEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnEstablishLabelSetConnection); + OnLooseLabelSetConnection(); + } +} + +QmitkSimpleLabelSetListWidget::LabelVectorType QmitkSimpleLabelSetListWidget::SelectedLabels() const +{ + auto selectedItems = m_LabelList->selectedItems(); + LabelVectorType result; + + QList::Iterator it; + for (it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + auto labelValue = (*it)->data(Qt::UserRole).toUInt(); + + + auto activeLayerID = m_LabelSetImage->GetActiveLayer(); + auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID); + + result.push_back(labelSet->GetLabel(labelValue)); + } + + return result; +} + +const mitk::LabelSetImage* QmitkSimpleLabelSetListWidget::GetLabelSetImage() const +{ + return m_LabelSetImage; +} + +void QmitkSimpleLabelSetListWidget::SetLabelSetImage(const mitk::LabelSetImage* image) +{ + if (image != m_LabelSetImage) + { + if (m_LabelSetImage.IsNotNull()) + { + m_LabelSetImage->BeforeChangeLayerEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection); + m_LabelSetImage->AfterChangeLayerEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLayerChanged); + this->OnLooseLabelSetConnection(); + } + + m_LabelSetImage = image; + + if (m_LabelSetImage.IsNotNull()) + { + m_LabelSetImage->BeforeChangeLayerEvent += mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection); + m_LabelSetImage->AfterChangeLayerEvent += mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLayerChanged); + this->OnLayerChanged(); + } + } +} + +void QmitkSimpleLabelSetListWidget::OnLooseLabelSetConnection() +{ + if (m_LabelSetImage.IsNull()) + return; + + auto activeLayerID = m_LabelSetImage->GetActiveLayer(); + auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID); + + // Reset LabelSetWidget Events + labelSet->AddLabelEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLabelChanged); + labelSet->RemoveLabelEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLabelChanged); + labelSet->ModifyLabelEvent -= mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLabelChanged); +} + +void QmitkSimpleLabelSetListWidget::OnEstablishLabelSetConnection() +{ + if (m_LabelSetImage.IsNull()) + return; + + auto activeLayerID = m_LabelSetImage->GetActiveLayer(); + auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID); + + // Reset LabelSetWidget Events + labelSet->AddLabelEvent += mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLabelChanged); + labelSet->RemoveLabelEvent += mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLabelChanged); + labelSet->ModifyLabelEvent += mitk::MessageDelegate( + this, &QmitkSimpleLabelSetListWidget::OnLabelChanged); +} + +void QmitkSimpleLabelSetListWidget::OnLayerChanged() +{ + this->OnEstablishLabelSetConnection(); + this->ResetList(); + emit ActiveLayerChanged(); + emit SelectedLabelsChanged(this->SelectedLabels()); +} + +void QmitkSimpleLabelSetListWidget::OnLabelChanged() +{ + this->ResetList(); + emit ActiveLayerChanged(); + emit SelectedLabelsChanged(this->SelectedLabels()); +} + +void QmitkSimpleLabelSetListWidget::OnLabelSelectionChanged() +{ + emit SelectedLabelsChanged(this->SelectedLabels()); +} + +void QmitkSimpleLabelSetListWidget::ResetList() +{ + m_LabelList->clear(); + + auto activeLayerID = m_LabelSetImage->GetActiveLayer(); + auto labelSet = m_LabelSetImage->GetLabelSet(activeLayerID); + + auto iter = labelSet->IteratorConstBegin(); + for (; iter != labelSet->IteratorConstEnd(); ++iter) + { + auto color = iter->second->GetColor(); + QPixmap pixmap(10, 10); + pixmap.fill(QColor(color[0] * 255, color[1] * 255, color[2] * 255)); + QIcon icon(pixmap); + + QListWidgetItem* item = new QListWidgetItem(icon, QString::fromStdString(iter->second->GetName())); + item->setData(Qt::UserRole, QVariant(iter->second->GetValue())); + m_LabelList->addItem(item); + } +} + +void QmitkSimpleLabelSetListWidget::SetSelectedLabels(const LabelVectorType& selectedLabels) +{ + for (int i = 0; i < m_LabelList->count(); ++i) + { + QListWidgetItem* item = m_LabelList->item(i); + auto labelValue = item->data(Qt::UserRole).toUInt(); + + auto finding = std::find_if(selectedLabels.begin(), selectedLabels.end(), [labelValue](const mitk::Label* label) {return label->GetValue() == labelValue; }); + item->setSelected(finding != selectedLabels.end()); + } +} + diff --git a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h new file mode 100644 index 0000000000..88c716ff0c --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h @@ -0,0 +1,63 @@ +/*============================================================================ + +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 QmitkSimpleLabelSetListWidget_h_Included +#define QmitkSimpleLabelSetListWidget_h_Included + +#include "mitkLabel.h" +#include "mitkLabelSetImage.h" +#include +#include + +/** + \brief Widget that offers a simple list that displays all labels (color and name) in the active + layer of a LabelSetImage. +*/ +class MITKSEGMENTATIONUI_EXPORT QmitkSimpleLabelSetListWidget : public QWidget +{ + Q_OBJECT + +public: + QmitkSimpleLabelSetListWidget(QWidget* parent = nullptr); + ~QmitkSimpleLabelSetListWidget() override; + + using LabelVectorType = std::vector; + + LabelVectorType SelectedLabels() const; + const mitk::LabelSetImage* GetLabelSetImage() const; + +signals: + void SelectedLabelsChanged(const LabelVectorType& selectedLabels); + void ActiveLayerChanged(); + +public slots : + void SetLabelSetImage(const mitk::LabelSetImage* image); + void SetSelectedLabels(const LabelVectorType& selectedLabels); + +protected slots: + + void OnLabelSelectionChanged(); + +protected: + void OnLayerChanged(); + void OnLabelChanged(); + + void OnLooseLabelSetConnection(); + void OnEstablishLabelSetConnection(); + + void ResetList(); + + mitk::LabelSetImage::ConstPointer m_LabelSetImage; + QListWidget* m_LabelList; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp index a6f7763f5c..41c3151292 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp @@ -1,150 +1,199 @@ /*============================================================================ 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 "QmitkWatershedToolGUI.h" -#include "QmitkNewSegmentationDialog.h" -#include "mitkProgressBar.h" - -#include -#include -#include -#include -#include -#include +#include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkWatershedToolGUI, "") -QmitkWatershedToolGUI::QmitkWatershedToolGUI() : QmitkToolGUI(), m_SliderThreshold(nullptr), m_SliderLevel(nullptr) +QmitkWatershedToolGUI::QmitkWatershedToolGUI() : QmitkToolGUI() { - // create the visible widgets - QGridLayout *layout = new QGridLayout(this); - this->setContentsMargins(0, 0, 0, 0); - - QLabel *label = new QLabel("Threshold ", this); - QFont f = label->font(); - f.setBold(false); - label->setFont(f); - layout->addWidget(label, 0, 0); - - QLabel *label2 = new QLabel("Level ", this); - f = label2->font(); - f.setBold(false); - label2->setFont(f); - layout->addWidget(label2, 2, 0); - - m_ThresholdLabel = new QLabel(" 0.04", this); - f = m_ThresholdLabel->font(); - f.setBold(false); - m_ThresholdLabel->setFont(f); - layout->addWidget(m_ThresholdLabel, 0, 1); - - m_SliderThreshold = new QSlider(Qt::Horizontal, this); - m_SliderThreshold->setMinimum(0); - m_SliderThreshold->setMaximum(100); - m_SliderThreshold->setPageStep(1); - m_SliderThreshold->setValue(4); - connect(m_SliderThreshold, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueThresholdChanged(int))); - layout->addWidget(m_SliderThreshold, 1, 0, 1, 2); - - m_LevelLabel = new QLabel(" 0.35", this); - f = m_LevelLabel->font(); - f.setBold(false); - m_LevelLabel->setFont(f); - layout->addWidget(m_LevelLabel, 2, 1); - - m_SliderLevel = new QSlider(Qt::Horizontal, this); - m_SliderLevel->setMinimum(0); - m_SliderLevel->setMaximum(100); - m_SliderLevel->setPageStep(1); - m_SliderLevel->setValue(35); - connect(m_SliderLevel, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueLevelChanged(int))); - layout->addWidget(m_SliderLevel, 3, 0, 1, 2); - - QPushButton *okButton = new QPushButton("Run Segmentation", this); - connect(okButton, SIGNAL(clicked()), this, SLOT(OnCreateSegmentation())); - okButton->setFont(f); - layout->addWidget(okButton, 4, 0, 1, 2); - - m_InformationLabel = new QLabel("", this); - f = m_InformationLabel->font(); - f.setBold(false); - m_InformationLabel->setFont(f); - layout->addWidget(m_InformationLabel, 5, 0, 1, 2); - + m_Controls.setupUi(this); + + m_Controls.thresholdSlider->setMinimum(0); + //We set the threshold maximum to 0.5 to avoid crashes in the watershed filter + //see T27703 for more details. + m_Controls.thresholdSlider->setMaximum(0.5); + m_Controls.thresholdSlider->setValue(m_Threshold); + m_Controls.thresholdSlider->setPageStep(0.01); + m_Controls.thresholdSlider->setSingleStep(0.001); + m_Controls.thresholdSlider->setDecimals(4); + + m_Controls.levelSlider->setMinimum(0); + m_Controls.levelSlider->setMaximum(1); + m_Controls.levelSlider->setValue(m_Level); + m_Controls.levelSlider->setPageStep(0.1); + m_Controls.levelSlider->setSingleStep(0.01); + + connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnSettingsAccept())); + connect(m_Controls.m_selectionListWidget, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkWatershedToolGUI::OnRegionSelectionChanged); + connect(m_Controls.levelSlider, SIGNAL(valueChanged(double)), this, SLOT(OnLevelChanged(double))); + connect(m_Controls.thresholdSlider, SIGNAL(valueChanged(double)), this, SLOT(OnThresholdChanged(double))); + connect(m_Controls.m_ConfSegButton, SIGNAL(clicked()), this, SLOT(OnSegmentationRegionAccept())); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkWatershedToolGUI::~QmitkWatershedToolGUI() { if (m_WatershedTool.IsNotNull()) { - // m_WatershedTool->SizeChanged -= mitk::MessageDelegate1( this, - // &QmitkWatershedToolGUI::OnSizeChanged ); + m_WatershedTool->CurrentlyBusy -= + mitk::MessageDelegate1(this, &QmitkWatershedToolGUI::BusyStateChanged); + } +} + +void QmitkWatershedToolGUI::OnRegionSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels) +{ + if (m_WatershedTool.IsNotNull()) + { + mitk::AutoMLSegmentationWithPreviewTool::SelectedLabelVectorType labelIDs; + for (const auto& label : selectedLabels) + { + labelIDs.push_back(label->GetValue()); + } + + m_WatershedTool->SetSelectedLabels(labelIDs); + m_WatershedTool->UpdatePreview(); + + m_Controls.m_ConfSegButton->setEnabled(!labelIDs.empty()); } } void QmitkWatershedToolGUI::OnNewToolAssociated(mitk::Tool *tool) { if (m_WatershedTool.IsNotNull()) { - // m_WatershedTool->SizeChanged -= mitk::MessageDelegate1( this, - // &QmitkWatershedToolGUI::OnSizeChanged ); + m_WatershedTool->CurrentlyBusy -= + mitk::MessageDelegate1(this, &QmitkWatershedToolGUI::BusyStateChanged); } m_WatershedTool = dynamic_cast(tool); - OnSliderValueLevelChanged(35); - OnSliderValueThresholdChanged(4); if (m_WatershedTool.IsNotNull()) { - // m_WatershedTool->SizeChanged += mitk::MessageDelegate1( this, - // &QmitkWatershedToolGUI::OnSizeChanged ); + m_WatershedTool->CurrentlyBusy += + mitk::MessageDelegate1(this, &QmitkWatershedToolGUI::BusyStateChanged); + + m_WatershedTool->SetLevel(m_Level); + m_WatershedTool->SetThreshold(m_Threshold); + + m_WatershedTool->SetOverwriteExistingSegmentation(true); + m_WatershedTool->IsTimePointChangeAwareOff(); + m_Controls.m_CheckProcessAll->setVisible(m_WatershedTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps() > 1); } } -void QmitkWatershedToolGUI::OnSliderValueThresholdChanged(int value) +void QmitkWatershedToolGUI::OnSegmentationRegionAccept() { + QString segName = QString::fromStdString(m_WatershedTool->GetCurrentSegmentationName()); + if (m_WatershedTool.IsNotNull()) { - double realValue = value / 100.; - m_WatershedTool->SetThreshold(realValue); - m_ThresholdLabel->setText(QString::number(realValue)); + if (this->m_Controls.m_CheckCreateNew->isChecked()) + { + m_WatershedTool->SetOverwriteExistingSegmentation(false); + } + else + { + m_WatershedTool->SetOverwriteExistingSegmentation(true); + } + + m_WatershedTool->SetCreateAllTimeSteps(this->m_Controls.m_CheckProcessAll->isChecked()); + + this->m_Controls.m_ConfSegButton->setEnabled(false); + m_WatershedTool->ConfirmSegmentation(); } } -void QmitkWatershedToolGUI::OnSliderValueLevelChanged(int value) +void QmitkWatershedToolGUI::OnSettingsAccept() { if (m_WatershedTool.IsNotNull()) { - double realValue = value / 100.; - m_WatershedTool->SetLevel(realValue); - m_LevelLabel->setText(QString::number(realValue)); + try + { + m_Threshold = m_Controls.thresholdSlider->value(); + m_Level = m_Controls.levelSlider->value(); + m_WatershedTool->SetThreshold(m_Threshold); + m_WatershedTool->SetLevel(m_Level); + + m_WatershedTool->UpdatePreview(); + } + catch (const std::exception& e) + { + this->setCursor(Qt::ArrowCursor); + std::stringstream stream; + stream << "Error while generation watershed segmentation. Reason: " << e.what(); + + QMessageBox* messageBox = + new QMessageBox(QMessageBox::Critical, + nullptr, stream.str().c_str()); + messageBox->exec(); + delete messageBox; + MITK_ERROR << stream.str(); + return; + } + catch (...) + { + this->setCursor(Qt::ArrowCursor); + std::stringstream stream; + stream << "Unkown error occured while generation watershed segmentation."; + + QMessageBox* messageBox = + new QMessageBox(QMessageBox::Critical, + nullptr, stream.str().c_str()); + messageBox->exec(); + delete messageBox; + MITK_ERROR << stream.str(); + return; + } + + m_Controls.m_selectionListWidget->SetLabelSetImage(m_WatershedTool->GetMLPreview()); + m_WatershedTool->IsTimePointChangeAwareOn(); } } -void QmitkWatershedToolGUI::OnCreateSegmentation() +void QmitkWatershedToolGUI::BusyStateChanged(bool value) { - QApplication::setOverrideCursor(Qt::BusyCursor); - m_InformationLabel->setText(QString("Please wait some time for computation...")); - m_InformationLabel->repaint(); - QApplication::processEvents(); + if (value) + { + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + } + else + { + QApplication::restoreOverrideCursor(); + } - m_WatershedTool->DoIt(); - m_InformationLabel->setText(QString("")); - QApplication::setOverrideCursor(Qt::ArrowCursor); + m_Controls.levelSlider->setEnabled(!value); + m_Controls.thresholdSlider->setEnabled(!value); + m_Controls.m_ConfSegButton->setEnabled(!m_WatershedTool->GetSelectedLabels().empty() && !value); + m_Controls.m_CheckProcessAll->setEnabled(!value); + m_Controls.m_CheckCreateNew->setEnabled(!value); + m_Controls.previewButton->setEnabled(!value); +} - for (int i = 0; i < 60; ++i) + +void QmitkWatershedToolGUI::OnLevelChanged(double value) +{ + if (m_WatershedTool.IsNotNull()) + { + m_WatershedTool->SetLevel(value); + } +} + +void QmitkWatershedToolGUI::OnThresholdChanged(double value) +{ + if (m_WatershedTool.IsNotNull()) { - mitk::ProgressBar::GetInstance()->Progress(); + m_WatershedTool->SetThreshold(value); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h index 23c40cab24..c5b8bbe5f1 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h @@ -1,72 +1,71 @@ /*============================================================================ 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 QmitkWatershedToolGUI_h_Included #define QmitkWatershedToolGUI_h_Included #include "QmitkToolGUI.h" #include "mitkWatershedTool.h" -#include -class QSlider; -class QLabel; -class QFrame; +#include "ui_QmitkWatershedToolGUIControls.h" + +#include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::WatershedTool. \sa mitk::WatershedTool This GUI shows two sliders to change the watershed parameters. It executes the watershed algorithm by clicking on the button. */ class MITKSEGMENTATIONUI_EXPORT QmitkWatershedToolGUI : public QmitkToolGUI { Q_OBJECT public: mitkClassMacro(QmitkWatershedToolGUI, QmitkToolGUI); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - protected slots : +protected slots : + + void OnNewToolAssociated(mitk::Tool *); - void OnNewToolAssociated(mitk::Tool *); + void OnSettingsAccept(); - /** \brief Passes the chosen threshold value directly to the watershed tool */ - void OnSliderValueThresholdChanged(int value); - /** \brief Passes the chosen level value directly to the watershed tool */ - void OnSliderValueLevelChanged(int value); - /** \brief Starts segmentation algorithm in the watershed tool */ - void OnCreateSegmentation(); + void OnSegmentationRegionAccept(); + + void OnRegionSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels); + + void OnLevelChanged(double value); + void OnThresholdChanged(double value); protected: QmitkWatershedToolGUI(); ~QmitkWatershedToolGUI() override; - QSlider *m_SliderThreshold; - QSlider *m_SliderLevel; - - /** \brief Label showing the current threshold value. */ - QLabel *m_ThresholdLabel; - /** \brief Label showing the current level value. */ - QLabel *m_LevelLabel; - /** \brief Label showing additional informations. */ - QLabel *m_InformationLabel; + void BusyStateChanged(bool value) override; - QFrame *m_Frame; + //Recommendation from ITK is to have a threshold:level ration around 1:100 + //we set Level a bit higher. This provokes more oversegmentation, + //but produces less objects in the first run and profits form the fact that + //decreasing level is quite fast in the filter. + double m_Level = 0.6; + double m_Threshold = 0.004; + Ui_QmitkWatershedToolGUIControls m_Controls; mitk::WatershedTool::Pointer m_WatershedTool; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUIControls.ui similarity index 52% copy from Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui copy to Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUIControls.ui index 27e1d87ff8..4c4931c08a 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkOtsuToolWidgetControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUIControls.ui @@ -1,234 +1,160 @@ - QmitkOtsuToolWidgetControls - + QmitkWatershedToolGUIControls + 0 0 192 - 300 + 352 0 0 100 0 100000 100000 QmitkOtsuToolWidget - - Move to adjust the segmentation - - - - QLayout::SetNoConstraint - - - - - - 0 - 0 - - + + + - Number of Regions: + Level: - - + + - + 0 0 - - - 40 - 16777215 - - - - 2 - - - 32 - - - - - - - - - - 0 - 0 - - - - - 0 - 32 - - - - Advanced settings - - - Qt::ToolButtonTextBesideIcon - - - true - - - - - - - - Use Valley Emphasis + Threshold: - - - - Number of Histogram Bins: - - + + - - - 2 - - - 4096 - - - 128 - - + - + - + 0 0 10000000 - 100 + 10000000 - - 0 - - - QAbstractItemView::MultiSelection - - - QListView::Adjust - - + 0 0 100000 16777215 Preview false - + 0 0 100000 16777215 Confirm Segmentation Process/overwrite all time steps of the dynamic segmentation and not just the currently visible time step. Process all time steps Add the confirmed segmentation as a new segmentation instead of overwriting the currently selected. Create as new segmentation - ctkExpandButton - QToolButton -
ctkExpandButton.h
+ QmitkSimpleLabelSetListWidget + QWidget +
QmitkSimpleLabelSetListWidget.h
+
+ + ctkSliderWidget + QWidget +
ctkSliderWidget.h
+ 1
diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index a7ba3f718d..d84231e8a4 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,81 +1,84 @@ set( CPP_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp Qmitk/QmitkBinaryThresholdToolGUI.cpp Qmitk/QmitkBinaryThresholdULToolGUI.cpp Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp Qmitk/QmitkConfirmSegmentationDialog.cpp Qmitk/QmitkCopyToClipBoardDialog.cpp Qmitk/QmitkDrawPaintbrushToolGUI.cpp Qmitk/QmitkErasePaintbrushToolGUI.cpp Qmitk/QmitkFastMarchingTool3DGUI.cpp Qmitk/QmitkFastMarchingToolGUI.cpp Qmitk/QmitkLiveWireTool2DGUI.cpp Qmitk/QmitkNewSegmentationDialog.cpp Qmitk/QmitkOtsuTool3DGUI.cpp Qmitk/QmitkPaintbrushToolGUI.cpp Qmitk/QmitkPickingToolGUI.cpp Qmitk/QmitkPixelManipulationToolGUI.cpp Qmitk/QmitkSlicesInterpolator.cpp Qmitk/QmitkToolGUI.cpp Qmitk/QmitkToolGUIArea.cpp Qmitk/QmitkToolSelectionBox.cpp Qmitk/QmitkWatershedToolGUI.cpp #Added from ML Qmitk/QmitkLabelSetWidget.cpp Qmitk/QmitkSurfaceStampWidget.cpp Qmitk/QmitkMaskStampWidget.cpp Qmitk/QmitkSliceBasedInterpolatorWidget.cpp Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp Qmitk/QmitkSearchLabelDialog.cpp +Qmitk/QmitkSimpleLabelSetListWidget.cpp ) set(MOC_H_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h Qmitk/QmitkBinaryThresholdToolGUI.h Qmitk/QmitkBinaryThresholdULToolGUI.h Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h Qmitk/QmitkConfirmSegmentationDialog.h Qmitk/QmitkCopyToClipBoardDialog.h Qmitk/QmitkDrawPaintbrushToolGUI.h Qmitk/QmitkErasePaintbrushToolGUI.h Qmitk/QmitkFastMarchingTool3DGUI.h Qmitk/QmitkFastMarchingToolGUI.h Qmitk/QmitkLiveWireTool2DGUI.h Qmitk/QmitkNewSegmentationDialog.h Qmitk/QmitkOtsuTool3DGUI.h Qmitk/QmitkPaintbrushToolGUI.h Qmitk/QmitkPickingToolGUI.h Qmitk/QmitkPixelManipulationToolGUI.h Qmitk/QmitkSlicesInterpolator.h Qmitk/QmitkToolGUI.h Qmitk/QmitkToolGUIArea.h Qmitk/QmitkToolSelectionBox.h Qmitk/QmitkWatershedToolGUI.h #Added from ML Qmitk/QmitkLabelSetWidget.h Qmitk/QmitkSurfaceStampWidget.h Qmitk/QmitkMaskStampWidget.h Qmitk/QmitkSliceBasedInterpolatorWidget.h Qmitk/QmitkSurfaceBasedInterpolatorWidget.h Qmitk/QmitkSearchLabelDialog.h +Qmitk/QmitkSimpleLabelSetListWidget.h ) set(UI_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui Qmitk/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkOtsuToolWidgetControls.ui Qmitk/QmitkPickingToolGUIControls.ui Qmitk/QmitkLiveWireTool2DGUIControls.ui +Qmitk/QmitkWatershedToolGUIControls.ui #Added from ML Qmitk/QmitkLabelSetWidgetControls.ui Qmitk/QmitkSurfaceStampWidgetGUIControls.ui Qmitk/QmitkMaskStampWidgetGUIControls.ui Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitkSearchLabelDialogGUI.ui ) set(QRC_FILES resources/SegmentationUI.qrc ) diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index 299883d318..d608a7078f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,903 +1,907 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkStatusBar.h" #include "QmitkNewSegmentationDialog.h" #include #include #include #include "QmitkSegmentationView.h" #include #include "mitkVtkResliceInterpolationProperty.h" #include "mitkApplicationCursor.h" #include "mitkSegmentationObjectFactory.h" #include "mitkPluginActivator.h" #include "mitkCameraController.h" #include "mitkLabelSetImage.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateSubGeometry.h" #include #include "usModuleResource.h" #include "usModuleResourceStream.h" //micro service to get the ToolManager instance #include "mitkToolManagerProvider.h" #include #include const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; QmitkSegmentationView::QmitkSegmentationView() : m_Parent(nullptr) , m_Controls(nullptr) , m_RenderWindowPart(nullptr) , m_MouseCursorSet(false) , m_DataSelectionChanged(false) { mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); m_IsNotAHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); m_IsOfTypeImagePredicate = mitk::NodePredicateAnd::New(validImages, m_IsNotAHelperObject); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinaryPredicate = mitk::NodePredicateNot::New(isBinaryPredicate); mitk::NodePredicateAnd::Pointer isABinaryImagePredicate = mitk::NodePredicateAnd::New(m_IsOfTypeImagePredicate, isBinaryPredicate); mitk::NodePredicateAnd::Pointer isNotABinaryImagePredicate = mitk::NodePredicateAnd::New(m_IsOfTypeImagePredicate, isNotBinaryPredicate); - m_IsASegmentationImagePredicate = mitk::NodePredicateOr::New(isABinaryImagePredicate, mitk::TNodePredicateDataType::New()); - m_IsAPatientImagePredicate = mitk::NodePredicateAnd::New(isNotABinaryImagePredicate, mitk::NodePredicateNot::New(mitk::TNodePredicateDataType::New())); + auto isMLImageType = mitk::TNodePredicateDataType::New(); + mitk::NodePredicateAnd::Pointer isAMLImagePredicate = mitk::NodePredicateAnd::New(isMLImageType, m_IsNotAHelperObject); + mitk::NodePredicateAnd::Pointer isNotAMLImagePredicate = mitk::NodePredicateAnd::New(mitk::NodePredicateNot::New(isMLImageType), m_IsNotAHelperObject); + + m_IsASegmentationImagePredicate = mitk::NodePredicateOr::New(isABinaryImagePredicate, isAMLImagePredicate); + m_IsAPatientImagePredicate = mitk::NodePredicateAnd::New(isNotABinaryImagePredicate, isNotAMLImagePredicate); } QmitkSegmentationView::~QmitkSegmentationView() { if (m_Controls) { SetToolSelectionBoxesEnabled(false); // deactivate all tools mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); // removing all observers for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_WorkingDataObserverTags.clear(); mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); SetToolManagerSelection(nullptr, nullptr); } delete m_Controls; } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; } if (m_Parent) { m_Parent->setEnabled(true); } // tell the interpolation about tool manager, data storage and render window part if (m_Controls) { mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage(this->GetDataStorage()); QList controllers; controllers.push_back(renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls->m_SlicesInterpolator->Initialize(toolManager, controllers); } } void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_RenderWindowPart = nullptr; if (m_Parent) { m_Parent->setEnabled(false); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (m_Controls != nullptr) { bool slimView = prefs->GetBool("slim view", false); m_Controls->m_ManualToolSelectionBox2D->SetShowNames(!slimView); m_Controls->m_ManualToolSelectionBox3D->SetShowNames(!slimView); m_Controls->btnNewSegmentation->setToolButtonStyle(slimView ? Qt::ToolButtonIconOnly : Qt::ToolButtonTextOnly); } auto autoSelectionEnabled = prefs->GetBool("auto selection", true); m_Controls->patImageSelector->SetAutoSelectNewNodes(autoSelectionEnabled); m_Controls->segImageSelector->SetAutoSelectNewNodes(autoSelectionEnabled); this->ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::ConstPointer referenceImage = dynamic_cast(node->GetData()); if (referenceImage.IsNotNull()) { if (referenceImage->GetDimension() > 1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog(m_Parent); // needs a QWidget as parent, "this" is not QWidget QStringList organColors = mitk::OrganNamesHandling::GetDefaultOrganColorString();; dialog->SetSuggestionList(organColors); int dialogReturnValue = dialog->exec(); if (dialogReturnValue == QDialog::Rejected) { // user clicked cancel or pressed Esc or something similar return; } const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); unsigned int imageTimeStep = 0; if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { auto result = QMessageBox::question(m_Parent, tr("Generate a static mask?"),tr("The selected image has multiple time steps. You can either generate a simple/static masks resembling the geometry of the first timestep of the image. Or you can generate a dynamic mask that equals the selected image in geometry and number of timesteps; thus a dynamic mask can change over time (e.g. according to the image)."), tr("Yes, generate a static mask"), tr("No, generate a dynamic mask"), QString(), 0,0); if (result == 0) { auto selector = mitk::ImageTimeSelector::New(); selector->SetInput(referenceImage); selector->SetTimeNr(0); selector->Update(); const auto refTimeGeometry = referenceImage->GetTimeGeometry(); auto newTimeGeometry = mitk::ProportionalTimeGeometry::New(); newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint()); newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint()); mitk::Image::Pointer newImage = selector->GetOutput(); newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0); newImage->SetTimeGeometry(newTimeGeometry); segTemplateImage = newImage; } } // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { std::string newNodeName = dialog->GetSegmentationName().toStdString(); if (newNodeName.empty()) { newNodeName = "no_name"; } mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode(segTemplateImage, newNodeName, dialog->GetColor()); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty("showVolume", mitk::BoolProperty::New(false)); if (!emptySegmentation) { return; // could be aborted by user } mitk::OrganNamesHandling::UpdateOrganList(organColors, dialog->GetSegmentationName(), dialog->GetColor()); // escape ';' here (replace by '\;'), see longer comment above QString stringForStorage = organColors.replaceInStrings(";", "\\;").join(";"); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->Put("Organ-Color-List", stringForStorage); this->GetPreferences()->Flush(); if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)) { mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDataStorage()->Add(emptySegmentation, node); // add as a child, because the segmentation "derives" from the original m_Controls->segImageSelector->SetCurrentSelectedNode(emptySegmentation); mitk::RenderingManager::GetInstance()->InitializeViews(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } catch (const std::bad_alloc&) { QMessageBox::warning(nullptr, tr("Create new segmentation"), tr("Could not allocate memory for new segmentation")); } } } else { QMessageBox::information(nullptr, tr("Segmentation"), tr("Segmentation is currently not supported for 2D images")); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnVisiblePropertyChanged() { this->CheckRenderingState(); } void QmitkSegmentationView::NodeAdded(const mitk::DataNode *node) { if (!m_IsASegmentationImagePredicate->CheckNode(node)) { return; } itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnVisiblePropertyChanged); m_WorkingDataObserverTags.insert(std::pair(const_cast(node), node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); ApplyDisplayOptions(const_cast(node)); } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { if (m_IsASegmentationImagePredicate->CheckNode(node)) { //First of all remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = nullptr; if ((mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0) == node) && m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { this->SetToolManagerSelection(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0), nullptr); this->UpdateWarningLabel(tr("Select or create a segmentation")); } mitk::Image* image = dynamic_cast(node->GetData()); mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image); } mitk::DataNode* tempNode = const_cast(node); //Remove observer if one was registered auto finding = m_WorkingDataObserverTags.find(tempNode); if (finding != m_WorkingDataObserverTags.end()) { node->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[tempNode]); m_WorkingDataObserverTags.erase(tempNode); } } void QmitkSegmentationView::OnPatientSelectionChanged(QList nodes) { if(! nodes.empty()) { this->UpdateWarningLabel(""); auto node = nodes.first(); auto segPredicate = mitk::NodePredicateAnd::New(m_IsASegmentationImagePredicate.GetPointer(), mitk::NodePredicateSubGeometry::New(node->GetData()->GetGeometry())); m_Controls->segImageSelector->SetNodePredicate(segPredicate); mitk::DataNode* segNode = m_Controls->segImageSelector->GetSelectedNode(); this->SetToolManagerSelection(node, segNode); if (segNode) { //Doing this we can assure that the segmentation is always visible if the segmentation and the patient image are //loaded separately int layer(10); node->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); this->CheckRenderingState(); } else { this->SetToolSelectionBoxesEnabled( false ); this->UpdateWarningLabel(tr("Select or create a segmentation")); } } else { m_Controls->segImageSelector->SetNodePredicate(m_IsASegmentationImagePredicate); this->UpdateWarningLabel(tr("Please select an image!")); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnSegmentationSelectionChanged(QList nodes) { if (nodes.empty()) { this->UpdateWarningLabel(tr("Select or create a segmentation")); this->SetToolSelectionBoxesEnabled( false ); return; } auto refNode = m_Controls->patImageSelector->GetSelectedNode(); auto segNode = nodes.front(); if (!refNode) { this->UpdateWarningLabel(tr("Please select the matching patient image!")); this->SetToolSelectionBoxesEnabled(false); this->SetToolManagerSelection(nullptr, segNode); return; } this->CheckRenderingState(); if ( m_Controls->lblSegmentationWarnings->isVisible()) // "this->CheckRenderingState()" caused a warning. we do not need to go any further return; this->SetToolManagerSelection(refNode, segNode); if (segNode) { //Doing this we can assure that the segmenation is always visible if the segmentation and the patient image are //loaded separately int layer(10); refNode->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); } else { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Select or create a segmentation")); } mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); if (!renderWindowPart || !segNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer())) { this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { QmitkRenderWindow* selectedRenderWindow = nullptr; QmitkRenderWindow* axialRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("axial"); QmitkRenderWindow* sagittalRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("sagittal"); QmitkRenderWindow* coronalRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("coronal"); QmitkRenderWindow* _3DRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d"); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, _3DRenderWindow->GetRenderer())) { selectedRenderWindow = _3DRenderWindow; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; { ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); context->ungetService(ppmRef); } selectedRenderWindow->GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes) { if (nodes.size() != 0) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at(0)->GetName(); if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0)) { this->OnContourMarkerSelected(nodes.at(0)); return; } } } void QmitkSegmentationView::OnTabWidgetChanged(int id) { //always disable tools on tab changed mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); //2D Tab ID = 0 //3D Tab ID = 1 if (id == 0) { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); //Deactivate possible active tool //TODO Remove possible visible interpolations -> Maybe changes in SlicesInterpolator } else { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox2D->hide(); m_Controls->m_ManualToolSelectionBox3D->show(); //Deactivate possible active tool } } void QmitkSegmentationView::SetToolManagerSelection(mitk::DataNode* referenceData, mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData(const_cast(workingData)); m_Controls->btnNewSegmentation->setEnabled(referenceData != nullptr); } void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent) { return; } // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, nullptr parent)) // if no segmentation is selected, do nothing if (!m_Controls) { return; // might happen on initialization (preferences loaded) } mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::DataNode::Pointer referenceData = toolManager->GetReferenceData(0); mitk::DataNode::Pointer workingData = toolManager->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_IsASegmentationImagePredicate); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility node->SetVisibility(node == referenceData); } } // 2. if (workingData.IsNotNull()) workingData->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) { return; } mitk::BoolProperty::Pointer drawOutline = mitk::BoolProperty::New(GetPreferences()->GetBool("draw outline", true)); mitk::BoolProperty::Pointer volumeRendering = mitk::BoolProperty::New(GetPreferences()->GetBool("volume rendering", false)); mitk::LabelSetImage* labelSetImage = dynamic_cast(node->GetData()); if (nullptr != labelSetImage) { // node is actually a multi label segmentation, // but its outline property can be set in the 'single label' segmentation preference page as well node->SetProperty("labelset.contour.active", drawOutline); //node->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); node->SetProperty("volumerendering", volumeRendering); // force render window update to show outline node->GetData()->Modified(); } else { // node is a 'single label' segmentation bool isBinary = false; node->GetBoolProperty("binary", isBinary); if (isBinary) { node->SetProperty("outline binary", drawOutline); node->SetProperty("outline width", mitk::FloatProperty::New(2.0)); //node->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); node->SetProperty("volumerendering", volumeRendering); // force render window update to show outline node->GetData()->Modified(); } } } void QmitkSegmentationView::CheckRenderingState() { mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); mitk::DataNode* workingNode = m_Controls->segImageSelector->GetSelectedNode(); if (!workingNode) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Select or create a segmentation")); return; } bool selectedNodeIsVisible = renderWindowPart && workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()); if (!selectedNodeIsVisible) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); return; } /* * Here we check whether the geometry of the selected segmentation image if aligned with the worldgeometry * At the moment it is not supported to use a geometry different from the selected image for reslicing. * For further information see Bug 16063 */ const mitk::BaseGeometry* worldGeo = this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (workingNode && worldGeo) { const mitk::BaseGeometry* workingNodeGeo = workingNode->GetData()->GetGeometry(); const mitk::BaseGeometry* worldGeo = this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true)) { this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), workingNode); this->SetToolSelectionBoxesEnabled(true); this->UpdateWarningLabel(""); return; } } this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), nullptr); this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!")); } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.size() == 0) m_Controls->lblSegmentationWarnings->hide(); else m_Controls->lblSegmentationWarnings->show(); m_Controls->lblSegmentationWarnings->setText("" + text + ""); } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->patImageSelector->SetDataStorage(GetDataStorage()); m_Controls->patImageSelector->SetNodePredicate(m_IsAPatientImagePredicate); m_Controls->patImageSelector->SetSelectionIsOptional(false); m_Controls->patImageSelector->SetInvalidInfo("Select an image."); m_Controls->patImageSelector->SetPopUpTitel("Select an image."); m_Controls->patImageSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation."); UpdateWarningLabel(tr("Please select an image")); if (m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { UpdateWarningLabel(tr("Select or create a new segmentation")); } m_Controls->segImageSelector->SetDataStorage(GetDataStorage()); m_Controls->segImageSelector->SetNodePredicate(m_IsASegmentationImagePredicate); m_Controls->segImageSelector->SetSelectionIsOptional(false); m_Controls->segImageSelector->SetInvalidInfo("Select a segmentation."); m_Controls->segImageSelector->SetPopUpTitel("Select a segmentation."); m_Controls->segImageSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected."); if (m_Controls->segImageSelector->GetSelectedNode().IsNotNull()) { UpdateWarningLabel(""); } mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert(toolManager); toolManager->SetDataStorage(*(GetDataStorage())); toolManager->InitializeTools(); QString segTools2D = tr("Add Subtract Correction Paint Wipe 'Region Growing' Fill Erase 'Live Wire' '2D Fast Marching'"); QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Fast Marching 3D' 'Region Growing 3D' Watershed Picking"); std::regex extSegTool2DRegEx("SegTool2D$"); std::regex extSegTool3DRegEx("SegTool3D$"); auto tools = toolManager->GetTools(); for (const auto &tool : tools) { if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx)) { segTools2D.append(QString(" '%1'").arg(tool->GetName())); } else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx)) { segTools3D.append(QString(" '%1'").arg(tool->GetName())); } } // all part of open source MITK m_Controls->m_ManualToolSelectionBox2D->setEnabled(true); m_Controls->m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer2D); m_Controls->m_ManualToolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString()); m_Controls->m_ManualToolSelectionBox2D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls->m_ManualToolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected, this, &QmitkSegmentationView::OnManualTool2DSelected); //setup 3D Tools m_Controls->m_ManualToolSelectionBox3D->setEnabled(true); m_Controls->m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer3D); //specify tools to be added to 3D Tool area m_Controls->m_ManualToolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); m_Controls->m_ManualToolSelectionBox3D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox3D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1(this, &QmitkSegmentationView::NewNodeObjectsGenerated); // create signal/slot connections connect(m_Controls->patImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnPatientSelectionChanged); connect(m_Controls->segImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnSegmentationSelectionChanged); connect(m_Controls->btnNewSegmentation, &QToolButton::clicked, this, &QmitkSegmentationView::CreateNewSegmentation); connect(m_Controls->tabWidgetSegmentationTools, &QTabWidget::currentChanged, this, &QmitkSegmentationView::OnTabWidgetChanged); connect(m_Controls->m_SlicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes); // set callback function for already existing nodes (images & segmentations) mitk::DataStorage::SetOfObjects::ConstPointer allImages = GetDataStorage()->GetSubset(m_IsOfTypeImagePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnVisiblePropertyChanged); m_WorkingDataObserverTags.insert(std::pair(node, node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); } itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::CheckRenderingState); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command); SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), m_Controls->segImageSelector->GetSelectedNode()); m_RenderWindowPart = GetRenderWindowPart(); if (m_RenderWindowPart) { RenderWindowPartActivated(m_RenderWindowPart); } //Should be done last, if everything else is configured because it triggers the autoselection of data. m_Controls->patImageSelector->SetAutoSelectNewNodes(true); m_Controls->segImageSelector->SetAutoSelectNewNodes(true); } void QmitkSegmentationView::SetFocus() { m_Controls->btnNewSegmentation->setFocus(); } void QmitkSegmentationView::OnManualTool2DSelected(int id) { if (id >= 0) { std::string text = "Active Tool: \""; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); text += toolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = toolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } else { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); } } void QmitkSegmentationView::ResetMouseCursor() { if ( m_MouseCursorSet ) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkSegmentationView::SetMouseCursor( const us::ModuleResource& resource, int hotspotX, int hotspotY ) { // Remove previously set mouse cursor if (m_MouseCursorSet) this->ResetMouseCursor(); if (resource) { us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } } void QmitkSegmentationView::SetToolSelectionBoxesEnabled(bool status) { if (status) { m_Controls->m_ManualToolSelectionBox2D->RecreateButtons(); m_Controls->m_ManualToolSelectionBox3D->RecreateButtons(); } m_Controls->m_ManualToolSelectionBox2D->setEnabled(status); m_Controls->m_ManualToolSelectionBox3D->setEnabled(status); m_Controls->m_SlicesInterpolator->setEnabled(status); }