diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp index 7addf943c8..2b6dc58665 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp @@ -1,182 +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 "mitkBooleanOperation.h" #include <itkNaryFunctorImageFilter.h> #include <mitkExceptionMacro.h> #include <mitkImageCast.h> #include <mitkImageTimeSelector.h> #include <mitkITKEventObserverGuard.h> using ITKGroupImageType = itk::Image<mitk::Label::PixelType, 3>; using BoolOpsFunctionType = mitk::Label::PixelType(const std::vector<mitk::Label::PixelType>& inputArray); /** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter. * For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter. */ template <class TPixelType> class BoolOpsFunctor { public: BoolOpsFunctor() {}; BoolOpsFunctor(std::function<BoolOpsFunctionType> opsFunction) : m_Function(opsFunction) { }; ~BoolOpsFunctor() {}; bool operator!=(const BoolOpsFunctor& other)const { return !(*this == other); } bool operator==(const BoolOpsFunctor& other) const { if ((this->m_Function.target_type() == other.m_Function.target_type()) && (this->m_Function.template target<BoolOpsFunctionType>() && other.m_Function.template target<BoolOpsFunctionType>()) // If both std::function objects hold function pointers of the same signature, // we can compare the pointers to check if they point to the same function. && (*this->m_Function.template target<BoolOpsFunctionType>() == *other.m_Function.template target<BoolOpsFunctionType>())) { return true; } return false; } BoolOpsFunctor& operator=(const BoolOpsFunctor& other) { this->m_Function = other.m_Function; return *this; } inline TPixelType operator()(const std::vector<TPixelType>& inputArray) { return m_Function(inputArray); } private: std::function<BoolOpsFunctionType> m_Function; }; -mitk::Image::Pointer GenerateInternal(const mitk::LabelSetImage* segmentation, mitk::LabelSetImage::LabelValueVectorType labeValues, std::function<BoolOpsFunctionType> opsFunction, +mitk::Image::Pointer GenerateInternal(const mitk::LabelSetImage* segmentation, mitk::LabelSetImage::LabelValueVectorType labelValues, std::function<BoolOpsFunctionType> opsFunction, std::function<void(float progress)> progressCallback = [](float) {}) { if (nullptr == segmentation) mitkThrow() << "Cannot perform boolean operation. Passed segmentation is not valid"; mitk::Image::Pointer result = mitk::Image::New(); result->Initialize(mitk::MakeScalarPixelType<mitk::LabelSetImage::LabelValueType>(), *(segmentation->GetTimeGeometry())); const auto timeStepCount = segmentation->GetTimeGeometry()->CountTimeSteps(); for (mitk::TimeStepType i = 0; i < timeStepCount; ++i) { using OpsFilterType = itk::NaryFunctorImageFilter<ITKGroupImageType, ITKGroupImageType, BoolOpsFunctor<mitk::LabelSetImage::LabelValueType> >; auto opsFilter = OpsFilterType::New(); - mitk::ITKEventObserverGuard eventGuard(opsFilter, itk::ProgressEvent(), [&opsFilter, progressCallback, timeStepCount](const itk::EventObject& /*event*/) - { progressCallback(opsFilter->GetProgress() / static_cast<float>(timeStepCount)); }); + mitk::ITKEventObserverGuard eventGuard(opsFilter, itk::ProgressEvent(), [&opsFilter, progressCallback, i, timeStepCount](const itk::EventObject& /*event*/) + { progressCallback(opsFilter->GetProgress() + static_cast<float>(i) / static_cast<float>(timeStepCount)); }); BoolOpsFunctor<mitk::LabelSetImage::LabelValueType> functor(opsFunction); opsFilter->SetFunctor(functor); std::vector < ITKGroupImageType::ConstPointer > inputImages; unsigned int inputIndex = 0; - for (auto value : labeValues) + for (auto value : labelValues) { auto groupImage = segmentation->GetGroupImage(segmentation->GetGroupIndexOfLabel(value)); auto groupImageAtTS = mitk::SelectImageByTimeStep(groupImage, i); ITKGroupImageType::Pointer itkImage; mitk::CastToItkImage(groupImageAtTS, itkImage); inputImages.push_back(itkImage); opsFilter->SetInput(inputIndex, itkImage); ++inputIndex; } opsFilter->Update(); auto resultTS = opsFilter->GetOutput(); result->SetVolume(resultTS->GetBufferPointer(), i); } return result; } mitk::Image::Pointer mitk::BooleanOperation::GenerateUnion(const LabelSetImage* segmentation, LabelSetImage::LabelValueVectorType labelValues, std::function<void(float progress)> progressCallback) { auto unionOps = [labelValues](const std::vector<mitk::Label::PixelType>& inputArray) { mitk::Label::PixelType result = 0; for (auto inIt = inputArray.cbegin(), refIt = labelValues.cbegin(); inIt != inputArray.cend(); ++inIt, ++refIt) { if (*inIt == *refIt) { result = 1; break; } } return result; }; return GenerateInternal(segmentation, labelValues, unionOps, progressCallback); } mitk::Image::Pointer mitk::BooleanOperation::GenerateIntersection(const LabelSetImage* segmentation, LabelSetImage::LabelValueVectorType labelValues, std::function<void(float progress)> progressCallback) { auto intersectOps = [labelValues](const std::vector<mitk::Label::PixelType>& inputArray) { mitk::Label::PixelType result = 1; for (auto inIt = inputArray.cbegin(), refIt = labelValues.cbegin(); inIt != inputArray.cend(); ++inIt, ++refIt) { if (*inIt != *refIt) { result = 0; break; } } return result; }; return GenerateInternal(segmentation, labelValues, intersectOps, progressCallback); } mitk::Image::Pointer mitk::BooleanOperation::GenerateDifference(const LabelSetImage* segmentation, LabelSetImage::LabelValueType minuendLabelValue, - LabelSetImage::LabelValueVectorType subtrahendLabelValues, std::function<void(float progress)> progressCallback) + const LabelSetImage::LabelValueVectorType subtrahendLabelValues, std::function<void(float progress)> progressCallback) { auto intersectOps = [minuendLabelValue, subtrahendLabelValues](const std::vector<mitk::Label::PixelType>& inputArray) { if (minuendLabelValue != inputArray.front()) return mitk::Label::PixelType(0); mitk::Label::PixelType result = 1; for (auto inIt = inputArray.cbegin()+1, refIt = subtrahendLabelValues.cbegin(); inIt != inputArray.cend(); ++inIt, ++refIt) { if (*inIt == *refIt) { result = 0; break; } } return result; }; auto labelValues = subtrahendLabelValues; labelValues.insert(labelValues.begin(), minuendLabelValue); return GenerateInternal(segmentation, labelValues, intersectOps, progressCallback); } diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h index 613e8d21bf..878133a987 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.h @@ -1,38 +1,38 @@ /*============================================================================ 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 mitkBooleanOperation_h #define mitkBooleanOperation_h #include <MitkSegmentationExports.h> #include <mitkLabelSetImage.h> namespace mitk { /** \brief Executes a boolean operation on two different segmentations. * All parameters of the boolean operations must be specified during construction. * The actual operation is executed when calling GetResult(). */ namespace BooleanOperation { - Image::Pointer MITKSEGMENTATION_EXPORT GenerateUnion(const LabelSetImage*, LabelSetImage::LabelValueVectorType labeValues, + Image::Pointer MITKSEGMENTATION_EXPORT GenerateUnion(const LabelSetImage*, LabelSetImage::LabelValueVectorType labelValues, std::function<void(float progress)> progressCallback = [](float) {}); Image::Pointer MITKSEGMENTATION_EXPORT GenerateIntersection(const LabelSetImage*, LabelSetImage::LabelValueVectorType labelValues, std::function<void(float progress)> progressCallback = [](float) {}); Image::Pointer MITKSEGMENTATION_EXPORT GenerateDifference(const LabelSetImage*, LabelSetImage::LabelValueType minuendLabelValue, - LabelSetImage::LabelValueVectorType subtrahendLabelValues, std::function<void(float progress)> progressCallback = [](float) {}); + const LabelSetImage::LabelValueVectorType subtrahendLabelValues, std::function<void(float progress)> progressCallback = [](float) {}); }; } #endif diff --git a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp index 7edf9e1b1c..ae6843eac7 100644 --- a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp +++ b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp @@ -1,259 +1,259 @@ /*============================================================================ 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 "QmitkBooleanOperationsWidget.h" #include <ui_QmitkBooleanOperationsWidgetControls.h> #include <mitkDataStorage.h> #include <mitkException.h> #include <mitkRenderingManager.h> #include <mitkMultiLabelPredicateHelper.h> #include <mitkBooleanOperation.h> #include <mitkProgressBar.h> #include <mitkLabelSetImageHelper.h> QmitkBooleanOperationsWidget::QmitkBooleanOperationsWidget(mitk::DataStorage* dataStorage, QWidget* parent) : QWidget(parent) { m_Controls = new Ui::QmitkBooleanOperationsWidgetControls; m_Controls->setupUi(this); m_Controls->label1st->setText("<img width=16 height=16 src=\":/Qmitk/BooleanLabelA_32x32.png\"/>"); m_Controls->label1st->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_Controls->label2nd->setText("<img width=16 height=16 src=\":/Qmitk/BooleanLabelB_32x32.png\"/>"); m_Controls->label2nd->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_Controls->segNodeSelector->SetDataStorage(dataStorage); m_Controls->segNodeSelector->SetNodePredicate(mitk::GetMultiLabelSegmentationPredicate()); m_Controls->segNodeSelector->SetSelectionIsOptional(false); m_Controls->segNodeSelector->SetInvalidInfo(QStringLiteral("Please select segmentation for extraction.")); m_Controls->segNodeSelector->SetPopUpTitel(QStringLiteral("Select segmentation")); m_Controls->segNodeSelector->SetPopUpHint(QStringLiteral("Select the segmentation that should be used as source for extraction.")); m_Controls->labelInspector->SetMultiSelectionMode(true); connect(m_Controls->segNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkBooleanOperationsWidget::OnSegSelectionChanged); connect(m_Controls->labelInspector, &QmitkMultiLabelInspector::CurrentSelectionChanged, this, &QmitkBooleanOperationsWidget::OnLabelSelectionChanged); - connect(m_Controls->differenceButton, SIGNAL(clicked()), this, SLOT(OnDifferenceButtonClicked())); - connect(m_Controls->intersectionButton, SIGNAL(clicked()), this, SLOT(OnIntersectionButtonClicked())); - connect(m_Controls->unionButton, SIGNAL(clicked()), this, SLOT(OnUnionButtonClicked())); + connect(m_Controls->differenceButton, &QToolButton::clicked, this, &QmitkBooleanOperationsWidget::OnDifferenceButtonClicked); + connect(m_Controls->intersectionButton, &QToolButton::clicked, this, &QmitkBooleanOperationsWidget::OnIntersectionButtonClicked); + connect(m_Controls->unionButton, &QToolButton::clicked, this, &QmitkBooleanOperationsWidget::OnUnionButtonClicked); m_Controls->segNodeSelector->SetAutoSelectNewNodes(true); this->ConfigureWidgets(); } QmitkBooleanOperationsWidget::~QmitkBooleanOperationsWidget() { m_Controls->labelInspector->SetMultiLabelNode(nullptr); } void QmitkBooleanOperationsWidget::OnSegSelectionChanged(QmitkAbstractNodeSelectionWidget::NodeList /*nodes*/) { auto node = m_Controls->segNodeSelector->GetSelectedNode(); m_Controls->labelInspector->SetMultiLabelNode(node); this->ConfigureWidgets(); } void QmitkBooleanOperationsWidget::OnLabelSelectionChanged(mitk::LabelSetImage::LabelValueVectorType /*labels*/) { this->ConfigureWidgets(); } namespace { std::string GenerateLabelHTML(const mitk::Label* label) { std::stringstream stream; auto color = label->GetColor(); stream << "<span style='color: #" << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(color.GetRed()*255) << std::setw(2) << static_cast<int>(color.GetGreen()*255) << std::setw(2) << static_cast<int>(color.GetBlue()*255) << "; font-size: 20px '>■</span>" << std::dec; stream << "<font class=\"normal\"> " << label->GetName()<< "</font>"; return stream.str(); } } void QmitkBooleanOperationsWidget::ConfigureWidgets() { auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto styleSheet = qApp->styleSheet(); m_Controls->line1stLabel->document()->setDefaultStyleSheet(styleSheet); m_Controls->lineOtherLabels->document()->setDefaultStyleSheet(styleSheet); if (selectedLabelValues.empty()) { m_Controls->line1stLabel->setHtml(QStringLiteral("<font class=\"warning\">Select 1st label to proceed.</font>")); } else { auto label = seg->GetLabel(selectedLabelValues.front()); m_Controls->line1stLabel->setText(QString::fromStdString(GenerateLabelHTML(label))); } if (selectedLabelValues.size() < 2) { m_Controls->lineOtherLabels->setHtml(QStringLiteral("<font class=\"warning\">Select secondary label(s) to proceed.</font>")); } else { std::stringstream stream; - for (mitk::LabelSetImage::LabelValueVectorType::iterator iter = selectedLabelValues.begin() + 1; iter != selectedLabelValues.end(); ++iter) + for (auto iter = selectedLabelValues.cbegin() + 1; iter != selectedLabelValues.cend(); ++iter) { auto label = seg->GetLabel(*iter); if (stream.rdbuf()->in_avail() != 0) stream << "; "; stream << GenerateLabelHTML(label); } m_Controls->lineOtherLabels->setText(QString::fromStdString(stream.str())); } m_Controls->differenceButton->setEnabled(selectedLabelValues.size()>1); m_Controls->intersectionButton->setEnabled(selectedLabelValues.size() > 1); m_Controls->unionButton->setEnabled(selectedLabelValues.size() > 1); } void QmitkBooleanOperationsWidget::OnDifferenceButtonClicked() { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); mitk::ProgressBar::GetInstance()->Reset(); mitk::ProgressBar::GetInstance()->AddStepsToDo(110); unsigned int currentProgress = 0; auto progressCallback = [¤tProgress](float filterProgress) { auto delta = (filterProgress * 100) - currentProgress; if (delta > 0) { currentProgress += delta; mitk::ProgressBar::GetInstance()->Progress(delta); } }; auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto minuend = selectedLabelValues.front(); auto subtrahends = mitk::LabelSetImage::LabelValueVectorType(selectedLabelValues.begin() + 1, selectedLabelValues.end()); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto resultMask = mitk::BooleanOperation::GenerateDifference(seg, minuend, subtrahends, progressCallback); std::stringstream name; - name << "Difference " << seg->GetLabel(minuend)->GetName() << " - "; + name << "Difference " << seg->GetLabel(minuend)->GetName() << " -"; for (auto label : subtrahends) { name << " " << seg->GetLabel(label)->GetName(); } this->SaveResultLabelMask(resultMask, name.str()); mitk::ProgressBar::GetInstance()->Reset(); QApplication::restoreOverrideCursor(); } void QmitkBooleanOperationsWidget::OnIntersectionButtonClicked() { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); mitk::ProgressBar::GetInstance()->Reset(); mitk::ProgressBar::GetInstance()->AddStepsToDo(110); unsigned int currentProgress = 0; auto progressCallback = [¤tProgress](float filterProgress) { auto delta = (filterProgress * 100) - currentProgress; if (delta > 0) { currentProgress += delta; mitk::ProgressBar::GetInstance()->Progress(delta); } }; auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto resultMask = mitk::BooleanOperation::GenerateIntersection(seg, selectedLabelValues, progressCallback); std::stringstream name; name << "Intersection"; for (auto label : selectedLabelValues) { name << " " << seg->GetLabel(label)->GetName(); } this->SaveResultLabelMask(resultMask, name.str()); mitk::ProgressBar::GetInstance()->Reset(); QApplication::restoreOverrideCursor(); } void QmitkBooleanOperationsWidget::OnUnionButtonClicked() { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); mitk::ProgressBar::GetInstance()->Reset(); mitk::ProgressBar::GetInstance()->AddStepsToDo(110); unsigned int currentProgress = 0; auto progressCallback = [¤tProgress](float filterProgress) { auto delta = (filterProgress * 100) - currentProgress; if (delta > 0) { currentProgress += delta; mitk::ProgressBar::GetInstance()->Progress(delta); } }; auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto resultMask = mitk::BooleanOperation::GenerateUnion(seg, selectedLabelValues, progressCallback); std::stringstream name; name << "Union"; for (auto label : selectedLabelValues) { name << " " << seg->GetLabel(label)->GetName(); } this->SaveResultLabelMask(resultMask, name.str()); mitk::ProgressBar::GetInstance()->Reset(); QApplication::restoreOverrideCursor(); } void QmitkBooleanOperationsWidget::SaveResultLabelMask(const mitk::Image* resultMask, const std::string& labelName) const { auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); if (seg == nullptr) mitkThrow() << "Widget is in invalid state. Processing was triggered with no segmentation selected."; auto labels = m_Controls->labelInspector->GetSelectedLabels(); if (labels.empty()) mitkThrow() << "Widget is in invalid state. Processing was triggered with no label selected."; auto groupID = seg->AddLayer(); auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(seg, labelName, true); seg->AddLabelWithContent(newLabel, resultMask, groupID, 1); m_Controls->labelInspector->GetMultiLabelSegmentation()->Modified(); m_Controls->labelInspector->GetMultiLabelNode()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); }