diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp index 6671c1604c..b533bbe242 100644 --- a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp +++ b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp @@ -1,158 +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"); } void mitk::WatershedTool::Activated() { Superclass::Activated(); 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"; } mitk::LabelSetImage::Pointer mitk::WatershedTool::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/) { mitk::LabelSetImage::Pointer labelSetOutput; try { mitk::Image::Pointer output; bool inputChanged = inputAtTimeStep != m_LastFilterInput; // create and run itk filter pipeline AccessByItk_2(inputAtTimeStep, ITKWatershed, output, inputChanged); labelSetOutput = mitk::LabelSetImage::New(); labelSetOutput->InitializeByLabeledImage(output); } 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; return labelSetOutput; } template void mitk::WatershedTool::ITKWatershed(const itk::Image* originalImage, mitk::Image::Pointer& segmentation, bool inputChanged) { typedef itk::WatershedImageFilter> WatershedFilter; typedef itk::GradientMagnitudeRecursiveGaussianImageFilter, 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 = 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(); } if (inputChanged) { magnitude->SetInput(originalImage); } // then add the watershed filter to the pipeline 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->Update(); // then make sure, that the output has the desired pixel type typedef itk::CastImageFilter> CastFilter; typename CastFilter::Pointer cast = CastFilter::New(); cast->SetInput(watershed->GetOutput()); // start the whole pipeline cast->Update(); // 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/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp index e76d08f4d9..41c3151292 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp @@ -1,197 +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 MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkWatershedToolGUI, "") QmitkWatershedToolGUI::QmitkWatershedToolGUI() : QmitkToolGUI() { m_Controls.setupUi(this); m_Controls.thresholdSlider->setMinimum(0); - m_Controls.thresholdSlider->setMaximum(1); + //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->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->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkWatershedToolGUI::BusyStateChanged); } m_WatershedTool = dynamic_cast(tool); if (m_WatershedTool.IsNotNull()) { 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::OnSegmentationRegionAccept() { QString segName = QString::fromStdString(m_WatershedTool->GetCurrentSegmentationName()); if (m_WatershedTool.IsNotNull()) { 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::OnSettingsAccept() { if (m_WatershedTool.IsNotNull()) { 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::BusyStateChanged(bool value) { if (value) { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); } else { QApplication::restoreOverrideCursor(); } 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); } void QmitkWatershedToolGUI::OnLevelChanged(double value) { if (m_WatershedTool.IsNotNull()) { m_WatershedTool->SetLevel(value); } } void QmitkWatershedToolGUI::OnThresholdChanged(double value) { if (m_WatershedTool.IsNotNull()) { m_WatershedTool->SetThreshold(value); } }