diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp index af9b161be2..6612d48157 100644 --- a/Modules/Multilabel/mitkLabelSetImage.cpp +++ b/Modules/Multilabel/mitkLabelSetImage.cpp @@ -1,1157 +1,1160 @@ /*============================================================================ 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 "mitkLabelSetImage.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImagePixelWriteAccessor.h" #include "mitkInteractionConst.h" #include "mitkLookupTableProperty.h" #include "mitkPadImageFilter.h" #include "mitkRenderingManager.h" #include "mitkDICOMSegmentationPropertyHelper.h" #include "mitkDICOMQIPropertyHelper.h" #include #include #include #include #include #include //#include #include #include template void SetToZero(itk::Image *source) { source->FillBuffer(0); } template void CreateLabelMaskProcessing(mitk::Image *layerImage, mitk::Image *mask, mitk::LabelSet::PixelType index) { mitk::ImagePixelReadAccessor readAccessor(layerImage); mitk::ImagePixelWriteAccessor writeAccessor(mask); std::size_t numberOfPixels = 1; for (int dim = 0; dim < static_cast(VImageDimension); ++dim) numberOfPixels *= static_cast(readAccessor.GetDimension(dim)); auto src = readAccessor.GetData(); auto dest = writeAccessor.GetData(); for (std::size_t i = 0; i < numberOfPixels; ++i) { if (index == *(src + i)) *(dest + i) = 1; } } mitk::LabelSetImage::LabelSetImage() : mitk::Image(), m_ActiveLayer(0), m_activeLayerInvalid(false), m_ExteriorLabel(nullptr) { // Iniitlaize Background Label mitk::Color color; color.Set(0, 0, 0); m_ExteriorLabel = mitk::Label::New(); m_ExteriorLabel->SetColor(color); m_ExteriorLabel->SetName("Exterior"); m_ExteriorLabel->SetOpacity(0.0); m_ExteriorLabel->SetLocked(false); m_ExteriorLabel->SetValue(0); // Add some DICOM Tags as properties to segmentation image DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this); } mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage &other) : Image(other), m_ActiveLayer(other.GetActiveLayer()), m_activeLayerInvalid(false), m_ExteriorLabel(other.GetExteriorLabel()->Clone()) { for (unsigned int i = 0; i < other.GetNumberOfLayers(); i++) { // Clone LabelSet data mitk::LabelSet::Pointer lsClone = other.GetLabelSet(i)->Clone(); // add modified event listener to LabelSet (listen to LabelSet changes) itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified); lsClone->AddObserver(itk::ModifiedEvent(), command); m_LabelSetContainer.push_back(lsClone); // clone layer Image data mitk::Image::Pointer liClone = other.GetLayerImage(i)->Clone(); m_LayerContainer.push_back(liClone); } // Add some DICOM Tags as properties to segmentation image DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this); } void mitk::LabelSetImage::OnLabelSetModified() { Superclass::Modified(); } void mitk::LabelSetImage::SetExteriorLabel(mitk::Label *label) { m_ExteriorLabel = label; } mitk::Label *mitk::LabelSetImage::GetExteriorLabel() { return m_ExteriorLabel; } const mitk::Label *mitk::LabelSetImage::GetExteriorLabel() const { return m_ExteriorLabel; } void mitk::LabelSetImage::Initialize(const mitk::Image *other) { mitk::PixelType pixelType(mitk::MakeScalarPixelType()); if (other->GetDimension() == 2) { const unsigned int dimensions[] = {other->GetDimension(0), other->GetDimension(1), 1}; Superclass::Initialize(pixelType, 3, dimensions); } else { Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions()); } auto originalGeometry = other->GetTimeGeometry()->Clone(); this->SetTimeGeometry(originalGeometry); // initialize image memory to zero if (4 == this->GetDimension()) { AccessFixedDimensionByItk(this, SetToZero, 4); } else { AccessByItk(this, SetToZero); } // Transfer some general DICOM properties from the source image to derived image (e.g. Patient information,...) DICOMQIPropertyHelper::DeriveDICOMSourceProperties(other, this); // Add a inital LabelSet ans corresponding image data to the stack - AddLayer(); + if (this->GetNumberOfLayers() == 0) + { + AddLayer(); + } } mitk::LabelSetImage::~LabelSetImage() { m_LabelSetContainer.clear(); } mitk::Image *mitk::LabelSetImage::GetLayerImage(unsigned int layer) { return m_LayerContainer[layer]; } const mitk::Image *mitk::LabelSetImage::GetLayerImage(unsigned int layer) const { return m_LayerContainer[layer]; } unsigned int mitk::LabelSetImage::GetActiveLayer() const { return m_ActiveLayer; } unsigned int mitk::LabelSetImage::GetNumberOfLayers() const { return m_LabelSetContainer.size(); } void mitk::LabelSetImage::RemoveLayer() { int layerToDelete = GetActiveLayer(); // remove all observers from active label set GetLabelSet(layerToDelete)->RemoveAllObservers(); // set the active layer to one below, if exists. if (layerToDelete != 0) { SetActiveLayer(layerToDelete - 1); } else { // we are deleting layer zero, it should not be copied back into the vector m_activeLayerInvalid = true; } // remove labelset and image data m_LabelSetContainer.erase(m_LabelSetContainer.begin() + layerToDelete); m_LayerContainer.erase(m_LayerContainer.begin() + layerToDelete); if (layerToDelete == 0) { this->SetActiveLayer(layerToDelete); } this->Modified(); } unsigned int mitk::LabelSetImage::AddLayer(mitk::LabelSet::Pointer labelSet) { mitk::Image::Pointer newImage = mitk::Image::New(); newImage->Initialize(this->GetPixelType(), this->GetDimension(), this->GetDimensions(), this->GetImageDescriptor()->GetNumberOfChannels()); newImage->SetTimeGeometry(this->GetTimeGeometry()->Clone()); if (newImage->GetDimension() < 4) { AccessByItk(newImage, SetToZero); } else { AccessFixedDimensionByItk(newImage, SetToZero, 4); } unsigned int newLabelSetId = this->AddLayer(newImage, labelSet); return newLabelSetId; } unsigned int mitk::LabelSetImage::AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer labelSet) { unsigned int newLabelSetId = m_LayerContainer.size(); // Add labelset to layer mitk::LabelSet::Pointer ls; if (labelSet.IsNotNull()) { ls = labelSet; } else { ls = mitk::LabelSet::New(); ls->AddLabel(GetExteriorLabel()); ls->SetActiveLabel(0 /*Exterior Label*/); } ls->SetLayer(newLabelSetId); // Add exterior Label to label set // mitk::Label::Pointer exteriorLabel = CreateExteriorLabel(); // push a new working image for the new layer m_LayerContainer.push_back(layerImage); // push a new labelset for the new layer m_LabelSetContainer.push_back(ls); // add modified event listener to LabelSet (listen to LabelSet changes) itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified); ls->AddObserver(itk::ModifiedEvent(), command); SetActiveLayer(newLabelSetId); // MITK_INFO << GetActiveLayer(); this->Modified(); return newLabelSetId; } void mitk::LabelSetImage::AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet) { if (m_LayerContainer.size() <= layerIdx) { mitkThrow() << "Trying to add labelSet to non-existing layer."; } if (layerIdx < m_LabelSetContainer.size()) { m_LabelSetContainer[layerIdx] = labelSet; } else { while (layerIdx >= m_LabelSetContainer.size()) { mitk::LabelSet::Pointer defaultLabelSet = mitk::LabelSet::New(); defaultLabelSet->AddLabel(GetExteriorLabel()); defaultLabelSet->SetActiveLabel(0 /*Exterior Label*/); defaultLabelSet->SetLayer(m_LabelSetContainer.size()); m_LabelSetContainer.push_back(defaultLabelSet); } m_LabelSetContainer.push_back(labelSet); } } void mitk::LabelSetImage::SetActiveLayer(unsigned int layer) { try { if (4 == this->GetDimension()) { if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers())) { BeforeChangeLayerEvent.Send(); if (m_activeLayerInvalid) { // We should not write the invalid layer back to the vector m_activeLayerInvalid = false; } else { AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (GetActiveLayer())); } m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter AccessFixedDimensionByItk_n(this, LayerContainerToImageProcessing, 4, (GetActiveLayer())); AfterChangeLayerEvent.Send(); } } else { if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers())) { BeforeChangeLayerEvent.Send(); if (m_activeLayerInvalid) { // We should not write the invalid layer back to the vector m_activeLayerInvalid = false; } else { AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer()); } m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer()); AfterChangeLayerEvent.Send(); } } } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImage::ClearBuffer() { try { if (this->GetDimension() == 4) { //remark: this extra branch was added, because LabelSetImage instances can be //dynamic (4D), but AccessByItk by support only supports 2D and 3D. //The option to change the CMake default dimensions for AccessByItk was //dropped (for details see discussion in T28756) AccessFixedDimensionByItk(this, ClearBufferProcessing,4); } else { AccessByItk(this, ClearBufferProcessing); } this->Modified(); } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } } bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue) const { bool exist = false; for (unsigned int lidx = 0; lidx < GetNumberOfLayers(); lidx++) exist |= m_LabelSetContainer[lidx]->ExistLabel(pixelValue); return exist; } bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue, unsigned int layer) const { bool exist = m_LabelSetContainer[layer]->ExistLabel(pixelValue); return exist; } bool mitk::LabelSetImage::ExistLabelSet(unsigned int layer) const { return layer < m_LabelSetContainer.size(); } void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, PixelType sourcePixelValue, unsigned int layer) { try { AccessByItk_2(this, MergeLabelProcessing, pixelValue, sourcePixelValue); } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } GetLabelSet(layer)->SetActiveLabel(pixelValue); Modified(); } void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, std::vector& vectorOfSourcePixelValues, unsigned int layer) { try { for (unsigned int idx = 0; idx < vectorOfSourcePixelValues.size(); idx++) { AccessByItk_2(this, MergeLabelProcessing, pixelValue, vectorOfSourcePixelValues[idx]); } } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } GetLabelSet(layer)->SetActiveLabel(pixelValue); Modified(); } void mitk::LabelSetImage::RemoveLabel(PixelType pixelValue, unsigned int layer) { this->GetLabelSet(layer)->RemoveLabel(pixelValue); this->EraseLabel(pixelValue); } void mitk::LabelSetImage::RemoveLabels(std::vector& VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++) { this->RemoveLabel(VectorOfLabelPixelValues[idx], layer); } } void mitk::LabelSetImage::EraseLabel(PixelType pixelValue) { try { if (4 == this->GetDimension()) { AccessFixedDimensionByItk_1(this, EraseLabelProcessing, 4, pixelValue); } else { AccessByItk_1(this, EraseLabelProcessing, pixelValue); } } catch (const itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } Modified(); } void mitk::LabelSetImage::EraseLabels(std::vector& VectorOfLabelPixelValues) { for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++) { this->EraseLabel(VectorOfLabelPixelValues[idx]); } } mitk::Label *mitk::LabelSetImage::GetActiveLabel(unsigned int layer) { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer]->GetActiveLabel(); } const mitk::Label* mitk::LabelSetImage::GetActiveLabel(unsigned int layer) const { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer]->GetActiveLabel(); } mitk::Label *mitk::LabelSetImage::GetLabel(PixelType pixelValue, unsigned int layer) const { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer]->GetLabel(pixelValue); } mitk::LabelSet *mitk::LabelSetImage::GetLabelSet(unsigned int layer) { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer].GetPointer(); } const mitk::LabelSet *mitk::LabelSetImage::GetLabelSet(unsigned int layer) const { if (m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer].GetPointer(); } mitk::LabelSet *mitk::LabelSetImage::GetActiveLabelSet() { if (m_LabelSetContainer.size() == 0) return nullptr; else return m_LabelSetContainer[GetActiveLayer()].GetPointer(); } const mitk::LabelSet* mitk::LabelSetImage::GetActiveLabelSet() const { if (m_LabelSetContainer.size() == 0) return nullptr; else return m_LabelSetContainer[GetActiveLayer()].GetPointer(); } void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue, unsigned int layer) { if (4 == this->GetDimension()) { AccessFixedDimensionByItk_2(this, CalculateCenterOfMassProcessing, 4, pixelValue, layer); } else { AccessByItk_2(this, CalculateCenterOfMassProcessing, pixelValue, layer); } } unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const { return m_LabelSetContainer[layer]->GetNumberOfLabels(); } unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const { unsigned int totalLabels(0); auto layerIter = m_LabelSetContainer.begin(); for (; layerIter != m_LabelSetContainer.end(); ++layerIter) totalLabels += (*layerIter)->GetNumberOfLabels(); return totalLabels; } void mitk::LabelSetImage::MaskStamp(mitk::Image *mask, bool forceOverwrite) { try { mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New(); padImageFilter->SetInput(0, mask); padImageFilter->SetInput(1, this); padImageFilter->SetPadConstant(0); padImageFilter->SetBinaryFilter(false); padImageFilter->SetLowerThreshold(0); padImageFilter->SetUpperThreshold(1); padImageFilter->Update(); mitk::Image::Pointer paddedMask = padImageFilter->GetOutput(); if (paddedMask.IsNull()) return; AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite); } catch (...) { mitkThrow() << "Could not stamp the provided mask on the selected label."; } } mitk::Image::Pointer mitk::LabelSetImage::CreateLabelMask(PixelType index, bool useActiveLayer, unsigned int layer) { auto previousActiveLayer = this->GetActiveLayer(); auto mask = mitk::Image::New(); try { // mask->Initialize(this) does not work here if this label set image has a single slice, // since the mask would be automatically flattened to a 2-d image, whereas we expect the // original dimension of this label set image. Hence, initialize the mask more explicitly: mask->Initialize(this->GetPixelType(), this->GetDimension(), this->GetDimensions()); mask->SetTimeGeometry(this->GetTimeGeometry()->Clone()); auto byteSize = sizeof(LabelSetImage::PixelType); for (unsigned int dim = 0; dim < mask->GetDimension(); ++dim) byteSize *= mask->GetDimension(dim); { ImageWriteAccessor accessor(mask); memset(accessor.GetData(), 0, byteSize); } if (!useActiveLayer) this->SetActiveLayer(layer); if (4 == this->GetDimension()) { ::CreateLabelMaskProcessing<4>(this, mask, index); } else if (3 == this->GetDimension()) { ::CreateLabelMaskProcessing(this, mask, index); } else { mitkThrow(); } } catch (...) { if (!useActiveLayer) this->SetActiveLayer(previousActiveLayer); mitkThrow() << "Could not create a mask out of the selected label."; } if (!useActiveLayer) this->SetActiveLayer(previousActiveLayer); return mask; } void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image) { if (image.IsNull() || image->IsEmpty() || !image->IsInitialized()) mitkThrow() << "Invalid labeled image."; try { this->Initialize(image); unsigned int byteSize = sizeof(LabelSetImage::PixelType); for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { byteSize *= image->GetDimension(dim); } mitk::ImageWriteAccessor *accessor = new mitk::ImageWriteAccessor(static_cast(this)); memset(accessor->GetData(), 0, byteSize); delete accessor; auto geometry = image->GetTimeGeometry()->Clone(); this->SetTimeGeometry(geometry); if (image->GetDimension() == 3) { AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3); } else if (image->GetDimension() == 4) { AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 4); } else { mitkThrow() << image->GetDimension() << "-dimensional label set images not yet supported"; } } catch (...) { mitkThrow() << "Could not intialize by provided labeled image."; } this->Modified(); } template void mitk::LabelSetImage::InitializeByLabeledImageProcessing(LabelSetImageType *labelSetImage, ImageType *image) { typedef itk::ImageRegionConstIteratorWithIndex SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; TargetIteratorType targetIter(labelSetImage, labelSetImage->GetRequestedRegion()); targetIter.GoToBegin(); SourceIteratorType sourceIter(image, image->GetRequestedRegion()); sourceIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { auto sourceValue = static_cast(sourceIter.Get()); targetIter.Set(sourceValue); if (!this->ExistLabel(sourceValue)) { std::stringstream name; name << "object-" << sourceValue; double rgba[4]; m_LabelSetContainer[this->GetActiveLayer()]->GetLookupTable()->GetTableValue(sourceValue, rgba); mitk::Color color; color.SetRed(rgba[0]); color.SetGreen(rgba[1]); color.SetBlue(rgba[2]); auto label = mitk::Label::New(); label->SetName(name.str().c_str()); label->SetColor(color); label->SetOpacity(rgba[3]); label->SetValue(sourceValue); this->GetLabelSet()->AddLabel(label); if (GetActiveLabelSet()->GetNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE || sourceValue >= mitk::Label::MAX_LABEL_VALUE) this->AddLayer(); } ++sourceIter; ++targetIter; } } template void mitk::LabelSetImage::MaskStampProcessing(ImageType *itkImage, mitk::Image *mask, bool forceOverwrite) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; SourceIteratorType sourceIter(itkMask, itkMask->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion()); targetIter.GoToBegin(); int activeLabel = this->GetActiveLabel(GetActiveLayer())->GetValue(); while (!sourceIter.IsAtEnd()) { PixelType sourceValue = sourceIter.Get(); PixelType targetValue = targetIter.Get(); if ((sourceValue != 0) && (forceOverwrite || !this->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels { targetIter.Set(activeLabel); } ++sourceIter; ++targetIter; } this->Modified(); } template void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType *itkImage, PixelType pixelValue, unsigned int layer) { // for now, we just retrieve the voxel in the middle typedef itk::ImageRegionConstIterator IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); std::vector indexVector; while (!iter.IsAtEnd()) { // TODO fix comparison warning more effective if (iter.Get() == pixelValue) { indexVector.push_back(iter.GetIndex()); } ++iter; } mitk::Point3D pos; pos.Fill(0.0); if (!indexVector.empty()) { typename itk::ImageRegionConstIteratorWithIndex::IndexType centerIndex; centerIndex = indexVector.at(indexVector.size() / 2); if (centerIndex.GetIndexDimension() == 3) { pos[0] = centerIndex[0]; pos[1] = centerIndex[1]; pos[2] = centerIndex[2]; } else return; } GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassIndex(pos); this->GetSlicedGeometry()->IndexToWorld(pos, pos); // TODO: TimeGeometry? GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos); } template void mitk::LabelSetImage::ClearBufferProcessing(ImageType *itkImage) { itkImage->FillBuffer(0); } template void mitk::LabelSetImage::LayerContainerToImageProcessing(itk::Image *target, unsigned int layer) { typedef itk::Image ImageType; typename ImageType::Pointer itkSource; // mitk::CastToItkImage(m_LayerContainer[layer], itkSource); itkSource = ImageToItkImage(m_LayerContainer[layer]); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; SourceIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(target, target->GetLargestPossibleRegion()); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } template void mitk::LabelSetImage::ImageToLayerContainerProcessing(itk::Image *source, unsigned int layer) const { typedef itk::Image ImageType; typename ImageType::Pointer itkTarget; // mitk::CastToItkImage(m_LayerContainer[layer], itkTarget); itkTarget = ImageToItkImage(m_LayerContainer[layer]); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; SourceIteratorType sourceIter(source, source->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion()); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } template void mitk::LabelSetImage::EraseLabelProcessing(ImageType *itkImage, PixelType pixelValue) { typedef itk::ImageRegionIterator IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { PixelType value = iter.Get(); if (value == pixelValue) { iter.Set(0); } ++iter; } } template void mitk::LabelSetImage::MergeLabelProcessing(ImageType *itkImage, PixelType pixelValue, PixelType index) { typedef itk::ImageRegionIterator IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { if (iter.Get() == index) { iter.Set(pixelValue); } ++iter; } } bool mitk::Equal(const mitk::LabelSetImage &leftHandSide, const mitk::LabelSetImage &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; /* LabelSetImage members */ MITK_INFO(verbose) << "--- LabelSetImage Equal ---"; // number layers returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers(); if (!returnValue) { MITK_INFO(verbose) << "Number of layers not equal."; return false; } // total number labels returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels(); if (!returnValue) { MITK_INFO(verbose) << "Total number of labels not equal."; return false; } // active layer returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer(); if (!returnValue) { MITK_INFO(verbose) << "Active layer not equal."; return false; } if (4 == leftHandSide.GetDimension()) { MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check."; } else { // working image data returnValue = mitk::Equal((const mitk::Image &)leftHandSide, (const mitk::Image &)rightHandSide, eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Working image data not equal."; return false; } } for (unsigned int layerIndex = 0; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++) { if (4 == leftHandSide.GetDimension()) { MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check."; } else { // layer image data returnValue = mitk::Equal(*leftHandSide.GetLayerImage(layerIndex), *rightHandSide.GetLayerImage(layerIndex), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Layer image data not equal."; return false; } } // layer labelset data returnValue = mitk::Equal(*leftHandSide.GetLabelSet(layerIndex), *rightHandSide.GetLabelSet(layerIndex), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Layer labelset data not equal."; return false; } } return returnValue; } /** 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 LabelTransferFunctor { public: LabelTransferFunctor() {}; LabelTransferFunctor(const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle, mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) : m_DestinationLabelSet(destinationLabelSet), m_SourceBackground(sourceBackground), m_DestinationBackground(destinationBackground), m_DestinationBackgroundLocked(destinationBackgroundLocked), m_SourceLabel(sourceLabel), m_NewDestinationLabel(newDestinationLabel), m_MergeStyle(mergeStyle), m_OverwriteStyle(overwriteStyle) { }; ~LabelTransferFunctor() {}; bool operator!=(const LabelTransferFunctor& other)const { return !(*this == other); } bool operator==(const LabelTransferFunctor& other) const { return this->m_SourceBackground == other.m_SourceBackground && this->m_DestinationBackground == other.m_DestinationBackground && this->m_DestinationBackgroundLocked == other.m_DestinationBackgroundLocked && this->m_SourceLabel == other.m_SourceLabel && this->m_NewDestinationLabel == other.m_NewDestinationLabel && this->m_MergeStyle == other.m_MergeStyle && this->m_OverwriteStyle == other.m_OverwriteStyle && this->m_DestinationLabelSet == other.m_DestinationLabelSet; } LabelTransferFunctor& operator=(const LabelTransferFunctor& other) { this->m_DestinationLabelSet = other.m_DestinationLabelSet; this->m_SourceBackground = other.m_SourceBackground; this->m_DestinationBackground = other.m_DestinationBackground; this->m_DestinationBackgroundLocked = other.m_DestinationBackgroundLocked; this->m_SourceLabel = other.m_SourceLabel; this->m_NewDestinationLabel = other.m_NewDestinationLabel; this->m_MergeStyle = other.m_MergeStyle; this->m_OverwriteStyle = other.m_OverwriteStyle; return *this; } inline TOutputpixel operator()(const TDestinationPixel& existingDestinationValue, const TSourcePixel& existingSourceValue) { if (existingSourceValue == this->m_SourceLabel) { if (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle) { return this->m_NewDestinationLabel; } else { auto label = this->m_DestinationLabelSet->GetLabel(existingDestinationValue); if (nullptr == label || !label->GetLocked()) { return this->m_NewDestinationLabel; } } } else if (mitk::MultiLabelSegmentation::MergeStyle::Replace == this->m_MergeStyle && existingSourceValue == this->m_SourceBackground && existingDestinationValue == this->m_NewDestinationLabel && (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle || !this->m_DestinationBackgroundLocked)) { return this->m_DestinationBackground; } return existingDestinationValue; } private: const mitk::LabelSet* m_DestinationLabelSet = nullptr; mitk::Label::PixelType m_SourceBackground = 0; mitk::Label::PixelType m_DestinationBackground = 0; bool m_DestinationBackgroundLocked = false; mitk::Label::PixelType m_SourceLabel = 1; mitk::Label::PixelType m_NewDestinationLabel = 1; mitk::MultiLabelSegmentation::MergeStyle m_MergeStyle = mitk::MultiLabelSegmentation::MergeStyle::Replace; mitk::MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks; }; /**Helper function used by TransferLabelContent to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/ template void TransferLabelContentHelper(const itk::Image* itkSourceImage, mitk::Image* destinationImage, const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle, mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) { typedef itk::Image ContentImageType; typename ContentImageType::Pointer itkDestinationImage; mitk::CastToItkImage(destinationImage, itkDestinationImage); typedef LabelTransferFunctor LabelTransferFunctorType; typedef itk::BinaryFunctorImageFilter FilterType; LabelTransferFunctorType transferFunctor(destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStyle); auto transferFilter = FilterType::New(); transferFilter->SetFunctor(transferFunctor); transferFilter->InPlaceOn(); transferFilter->SetInput1(itkDestinationImage); transferFilter->SetInput2(itkSourceImage); transferFilter->Update(); } void mitk::TransferLabelContent( const Image* sourceImage, Image* destinationImage, const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, std::vector > labelMapping, MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye, const TimeStepType timeStep) { if (nullptr == sourceImage) { mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null."; } if (nullptr == destinationImage) { mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null."; } if (nullptr == destinationLabelSet) { mitkThrow() << "Invalid call of TransferLabelContent; destinationLabelSet must not be null"; } Image::ConstPointer sourceImageAtTimeStep = SelectImageByTimeStep(sourceImage, timeStep); Image::Pointer destinationImageAtTimeStep = SelectImageByTimeStep(destinationImage, timeStep); if (nullptr == sourceImageAtTimeStep) { mitkThrow() << "Invalid call of TransferLabelContent; sourceImage does not have the requested time step: " << timeStep; } if (nullptr == destinationImageAtTimeStep) { mitkThrow() << "Invalid call of TransferLabelContent; destinationImage does not have the requested time step: " << timeStep; } for (const auto& [sourceLabel, newDestinationLabel] : labelMapping) { if (nullptr == destinationLabelSet->GetLabel(newDestinationLabel)) { mitkThrow() << "Invalid call of TransferLabelContent. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel; } AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye)); } destinationImage->Modified(); } void mitk::TransferLabelContent( const LabelSetImage* sourceImage, LabelSetImage* destinationImage, std::vector > labelMapping, MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye, const TimeStepType timeStep) { if (nullptr == sourceImage) { mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null."; } const auto sourceBackground = sourceImage->GetExteriorLabel()->GetValue(); const auto destinationBackground = destinationImage->GetExteriorLabel()->GetValue(); const auto destinationBackgroundLocked = destinationImage->GetExteriorLabel()->GetLocked(); const auto destinationLabelSet = destinationImage->GetLabelSet(destinationImage->GetActiveLayer()); for (const auto& mappingElement : labelMapping) { if (!sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer())) { mitkThrow() << "Invalid call of TransferLabelContent. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first; } } TransferLabelContent(sourceImage, destinationImage, destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked, labelMapping, mergeStyle, overwriteStlye, timeStep); } diff --git a/Modules/Segmentation/Interactions/mitkAdaptiveRegionGrowingTool.cpp b/Modules/Segmentation/Interactions/mitkAdaptiveRegionGrowingTool.cpp deleted file mode 100644 index 579561cfd0..0000000000 --- a/Modules/Segmentation/Interactions/mitkAdaptiveRegionGrowingTool.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/*============================================================================ - -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 "mitkAdaptiveRegionGrowingTool.h" -#include "mitkImage.h" -#include "mitkProperties.h" -#include "mitkToolManager.h" -// us -#include -#include -#include -#include - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, AdaptiveRegionGrowingTool, "AdaptiveRegionGrowingTool"); -} - -mitk::AdaptiveRegionGrowingTool::AdaptiveRegionGrowingTool() -{ - m_PointSetNode = mitk::DataNode::New(); - m_PointSetNode->GetPropertyList()->SetProperty("name", mitk::StringProperty::New("3D_Regiongrowing_Seedpoint")); - m_PointSetNode->GetPropertyList()->SetProperty("helper object", mitk::BoolProperty::New(true)); - m_PointSet = mitk::PointSet::New(); - m_PointSetNode->SetData(m_PointSet); -} - -mitk::AdaptiveRegionGrowingTool::~AdaptiveRegionGrowingTool() -{ -} - -bool mitk::AdaptiveRegionGrowingTool::CanHandle(const BaseData* referenceData, const BaseData* /*workingData*/) const -{ - if (referenceData == nullptr) - return false; - - auto *image = dynamic_cast(referenceData); - - if (image == nullptr) - return false; - - if (image->GetDimension() < 3) - return false; - - if (image->GetTimeSteps() > 1) //release quickfix for T28275 - return false; - - return true; -} - -const char **mitk::AdaptiveRegionGrowingTool::GetXPM() const -{ - return nullptr; -} - -const char *mitk::AdaptiveRegionGrowingTool::GetName() const -{ - return "Region Growing 3D"; -} - -us::ModuleResource mitk::AdaptiveRegionGrowingTool::GetIconResource() const -{ - us::Module *module = us::GetModuleContext()->GetModule(); - us::ModuleResource resource = module->GetResource("RegionGrowing_48x48.png"); - return resource; -} - -void mitk::AdaptiveRegionGrowingTool::Activated() -{ - Superclass::Activated(); - - if (!GetDataStorage()->Exists(m_PointSetNode)) - GetDataStorage()->Add(m_PointSetNode, GetWorkingData()); - m_SeedPointInteractor = mitk::SinglePointDataInteractor::New(); - m_SeedPointInteractor->LoadStateMachine("PointSet.xml"); - m_SeedPointInteractor->SetEventConfig("PointSetConfig.xml"); - m_SeedPointInteractor->SetDataNode(m_PointSetNode); -} - -void mitk::AdaptiveRegionGrowingTool::Deactivated() -{ - m_PointSet->Clear(); - GetDataStorage()->Remove(m_PointSetNode); - - Superclass::Deactivated(); -} - -void mitk::AdaptiveRegionGrowingTool::ConfirmSegmentation() -{ - this->GetToolManager()->ActivateTool(-1); -} - -mitk::DataNode *mitk::AdaptiveRegionGrowingTool::GetReferenceData() -{ - return this->GetToolManager()->GetReferenceData(0); -} - -mitk::DataStorage *mitk::AdaptiveRegionGrowingTool::GetDataStorage() -{ - return this->GetToolManager()->GetDataStorage(); -} - -mitk::DataNode *mitk::AdaptiveRegionGrowingTool::GetWorkingData() -{ - return this->GetToolManager()->GetWorkingData(0); -} - -mitk::DataNode::Pointer mitk::AdaptiveRegionGrowingTool::GetPointSetNode() -{ - return m_PointSetNode; -} diff --git a/Modules/Segmentation/Interactions/mitkAdaptiveRegionGrowingTool.h b/Modules/Segmentation/Interactions/mitkAdaptiveRegionGrowingTool.h deleted file mode 100644 index 7924978442..0000000000 --- a/Modules/Segmentation/Interactions/mitkAdaptiveRegionGrowingTool.h +++ /dev/null @@ -1,128 +0,0 @@ -/*============================================================================ - -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 mitkAdaptiveRegionGrowingTool_h_Included -#define mitkAdaptiveRegionGrowingTool_h_Included - -#include "mitkAutoSegmentationTool.h" -#include "mitkCommon.h" -#include "mitkDataStorage.h" -#include "mitkPointSet.h" -#include "mitkSinglePointDataInteractor.h" -#include - -namespace us -{ - class ModuleResource; -} - -namespace mitk -{ - /** - \brief Dummy Tool for AdaptiveRegionGrowingToolGUI to get Tool functionality for AdaptiveRegionGrowing. - The actual logic is implemented in QmitkAdaptiveRegionGrowingToolGUI. - - \ingroup ToolManagerEtAl - \sa mitk::Tool - \sa QmitkInteractiveSegmentation - - */ - class MITKSEGMENTATION_EXPORT AdaptiveRegionGrowingTool : public AutoSegmentationTool - { - public: - /** - * @brief mitkClassMacro - */ - mitkClassMacro(AdaptiveRegionGrowingTool, AutoSegmentationTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; - - /** - * @brief Get XPM - * @return nullptr - */ - const char **GetXPM() const override; - - /** - * @brief Get name - * @return name of the Tool - */ - const char *GetName() const override; - - /** - * @brief Get icon resource - * @return the resource Object of the Icon - */ - us::ModuleResource GetIconResource() const override; - - /** - * @brief Adds interactor for the seedpoint and creates a seedpoint if neccessary. - * - * - */ - void Activated() override; - - /** - * @brief Removes all set points and interactors. - * - * - */ - void Deactivated() override; - - /** - * @brief get pointset node - * @return the point set node - */ - virtual DataNode::Pointer GetPointSetNode(); - - /** - * @brief get reference data - * @return the current reference data. - */ - mitk::DataNode *GetReferenceData(); - - /** - * @brief Get working data - * @return a list of all working data. - */ - mitk::DataNode *GetWorkingData(); - - /** - * @brief Get datastorage - * @return the current data storage. - */ - mitk::DataStorage *GetDataStorage(); - - void ConfirmSegmentation(); - - protected: - /** - * @brief constructor - */ - AdaptiveRegionGrowingTool(); // purposely hidden - - /** - * @brief destructor - */ - ~AdaptiveRegionGrowingTool() override; - - private: - PointSet::Pointer m_PointSet; - SinglePointDataInteractor::Pointer m_SeedPointInteractor; - DataNode::Pointer m_PointSetNode; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp deleted file mode 100644 index 903be794ef..0000000000 --- a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/*============================================================================ - -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 -#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); - - this->GetToolManager()->GetDataStorage()->Add(m_MLPreviewNode); -} - -void mitk::AutoMLSegmentationWithPreviewTool::Deactivated() -{ - this->GetToolManager()->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->ResetPreviewContent(); - } -} - -void mitk::AutoMLSegmentationWithPreviewTool::SetNodeProperties(LabelSetImage::Pointer newMLPreview) -{ - 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->GetStatistics()->GetScalarValueMax()); - levWinProp->SetLevelWindow(levelwindow); - this->m_MLPreviewNode->SetProperty("levelwindow", levWinProp); - } -} - -void mitk::AutoMLSegmentationWithPreviewTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, 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); - this->SetNodeProperties(newMLPreview); - } - - 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(this->GetUserDefinedActiveLabel()); - 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(this->GetUserDefinedActiveLabel()); - 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 deleted file mode 100644 index 60921765c6..0000000000 --- a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.h +++ /dev/null @@ -1,82 +0,0 @@ -/*============================================================================ - -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, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) override; - virtual void SetNodeProperties(LabelSetImage::Pointer); - /** 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/mitkAutoSegmentationTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp deleted file mode 100644 index ef35be95dc..0000000000 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkAutoSegmentationTool.h" -#include "mitkImage.h" -#include "mitkToolManager.h" -#include -#include - -mitk::AutoSegmentationTool::AutoSegmentationTool() : Tool("dummy"), m_OverwriteExistingSegmentation(false) -{ -} - -mitk::AutoSegmentationTool::AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_OverwriteExistingSegmentation(false) -{ -} - -mitk::AutoSegmentationTool::~AutoSegmentationTool() -{ -} - -void mitk::AutoSegmentationTool::Activated() -{ - Superclass::Activated(); - - m_NoneOverwriteTargetSegmentationNode = nullptr; -} - -void mitk::AutoSegmentationTool::Deactivated() -{ - m_NoneOverwriteTargetSegmentationNode = nullptr; - - Superclass::Deactivated(); -} - -const char *mitk::AutoSegmentationTool::GetGroup() const -{ - return "autoSegmentation"; -} - -mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimeStep(const mitk::Image* image, unsigned int timestep) -{ - return SelectImageByTimeStep(image, timestep); -} - -mitk::Image::Pointer mitk::AutoSegmentationTool::GetImageByTimeStep(mitk::Image* image, unsigned int timestep) -{ - return SelectImageByTimeStep(image, timestep); -} - -mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint) -{ - return SelectImageByTimeStep(image, timePoint); -} - -void mitk::AutoSegmentationTool::SetOverwriteExistingSegmentation(bool overwrite) -{ - if (m_OverwriteExistingSegmentation != overwrite) - { - m_OverwriteExistingSegmentation = overwrite; - m_NoneOverwriteTargetSegmentationNode = nullptr; - } -} - -std::string mitk::AutoSegmentationTool::GetCurrentSegmentationName() -{ - if (this->GetToolManager()->GetWorkingData(0)) - return this->GetToolManager()->GetWorkingData(0)->GetName(); - else - return ""; -} - -mitk::DataNode *mitk::AutoSegmentationTool::GetTargetSegmentationNode() const -{ - mitk::DataNode::Pointer segmentationNode = this->GetToolManager()->GetWorkingData(0); - if (!m_OverwriteExistingSegmentation) - { - if (m_NoneOverwriteTargetSegmentationNode.IsNull()) - { - mitk::DataNode::Pointer refNode = this->GetToolManager()->GetReferenceData(0); - if (refNode.IsNull()) - { - // TODO create and use segmentation exceptions instead!! - MITK_ERROR << "No valid reference data!"; - return nullptr; - } - std::string nodename = refNode->GetName() + "_" + this->GetName(); - - const auto labelSetImage = dynamic_cast(segmentationNode->GetData()); - if (nullptr == labelSetImage) - { - //TODO: this part of the if statement is old legacy code and should be removed. - //Keept because I didn't want to break/rework to many things before - //the release 2022.04. Should be removed when the seg tool classes are streamlined and the - //multi data structure is the only one used in seg APIs and code. - - mitk::Color color; - color.SetRed(1); - color.SetBlue(0); - color.SetGreen(0); - //create a new segmentation node based on the current segmentation as template - m_NoneOverwriteTargetSegmentationNode = CreateEmptySegmentationNode(dynamic_cast(segmentationNode->GetData()), nodename, color); - } - else - { - auto clonedSegmentation = labelSetImage->Clone(); - m_NoneOverwriteTargetSegmentationNode = LabelSetImageHelper::CreateEmptySegmentationNode(nodename); - m_NoneOverwriteTargetSegmentationNode->SetData(clonedSegmentation); - } - } - segmentationNode = m_NoneOverwriteTargetSegmentationNode; - } - return segmentationNode; -} - -void mitk::AutoSegmentationTool::EnsureTargetSegmentationNodeInDataStorage() const -{ - auto targetNode = this->GetTargetSegmentationNode(); - if (!this->GetToolManager()->GetDataStorage()->Exists(targetNode)) - { - this->GetToolManager()->GetDataStorage()->Add(targetNode, this->GetToolManager()->GetReferenceData(0)); - } -} diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h deleted file mode 100644 index 8665208340..0000000000 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h +++ /dev/null @@ -1,86 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef mitkAutoSegmentationTool_h_Included -#define mitkAutoSegmentationTool_h_Included - -#include "mitkCommon.h" -#include "mitkTool.h" -#include - -namespace mitk -{ - class Image; - - /** - \brief Superclass for tool that create a new segmentation without user interaction in render windows - - This class is undocumented. Ask the creator ($Author$) to supply useful comments. - */ - class MITKSEGMENTATION_EXPORT AutoSegmentationTool : public Tool - { - public: - mitkClassMacro(AutoSegmentationTool, Tool); - - void Activated() override; - void Deactivated() override; - - /** This function controls wether a confirmed segmentation should replace the old - * segmentation/working node (true) or if it should be stored as new and additional - * node (false). - */ - void SetOverwriteExistingSegmentation(bool overwrite); - - /** - * @brief Gets the name of the currently selected segmentation node - * @return the name of the segmentation node or an empty string if - * none is selected - */ - std::string GetCurrentSegmentationName(); - - /** - * @brief Depending on the selected mode either returns the currently selected segmentation node - * or (if overwrite mode is false) creates a new one from the selected reference data. - * @remark Please keep in mind that new created nodes are not automatically added to the data storage. - * Derived tools can call EnsureTargetSegmentationNodeInDataStorage to ensure it as soon as it is clear - * that the target segmentation node will be/is confirmed. - * @return a mitk::DataNode which contains a segmentation image - */ - virtual DataNode *GetTargetSegmentationNode() const; - - protected: - AutoSegmentationTool(); // purposely hidden - AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden - ~AutoSegmentationTool() override; - - const char *GetGroup() const override; - - /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ - static Image::ConstPointer GetImageByTimeStep(const Image* image, unsigned int timestep); - /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ - static Image::Pointer GetImageByTimeStep(Image* image, unsigned int timestep); - /** Helper that extracts the image for the passed time point, if the image has multiple time steps.*/ - static Image::ConstPointer GetImageByTimePoint(const Image* image, TimePointType timePoint); - - void EnsureTargetSegmentationNodeInDataStorage() const; - - bool m_OverwriteExistingSegmentation; - - private: - /**Contains the node returned by GetTargetSementationNode if m_OverwriteExistingSegmentation == false. Then - * GetTargetSegmentation generates a new target segmentation node.*/ - mutable DataNode::Pointer m_NoneOverwriteTargetSegmentationNode; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp index cbc5a650cb..baad32e9c2 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp @@ -1,122 +1,122 @@ /*============================================================================ 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 "mitkBinaryThresholdBaseTool.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkLabelSetImage.h" #include #include mitk::BinaryThresholdBaseTool::BinaryThresholdBaseTool() : m_SensibleMinimumThreshold(-100), m_SensibleMaximumThreshold(+100), m_LowerThreshold(1), m_UpperThreshold(1) { } mitk::BinaryThresholdBaseTool::~BinaryThresholdBaseTool() { } void mitk::BinaryThresholdBaseTool::SetThresholdValues(double lower, double upper) { /* If value is not in the min/max range, do nothing. In that case, this method will be called again with a proper value right after. The only known case where this happens is with an [0.0, 1.0[ image, where value could be an epsilon greater than the max. */ if (lower < m_SensibleMinimumThreshold || lower > m_SensibleMaximumThreshold || upper < m_SensibleMinimumThreshold || upper > m_SensibleMaximumThreshold) { return; } m_LowerThreshold = lower; m_UpperThreshold = upper; if (nullptr != this->GetPreviewSegmentation()) { UpdatePreview(); } } void mitk::BinaryThresholdBaseTool::InitiateToolByInput() { const auto referenceImage = this->GetReferenceData(); if (nullptr != referenceImage) { m_SensibleMinimumThreshold = std::numeric_limits::max(); m_SensibleMaximumThreshold = std::numeric_limits::lowest(); Image::StatisticsHolderPointer statistics = referenceImage->GetStatistics(); for (unsigned int ts = 0; ts < referenceImage->GetTimeSteps(); ++ts) { m_SensibleMinimumThreshold = std::min(m_SensibleMinimumThreshold, static_cast(statistics->GetScalarValueMin())); m_SensibleMaximumThreshold = std::max(m_SensibleMaximumThreshold, static_cast(statistics->GetScalarValueMax())); } if (m_LockedUpperThreshold) { m_LowerThreshold = (m_SensibleMaximumThreshold + m_SensibleMinimumThreshold) / 2.0; m_UpperThreshold = m_SensibleMaximumThreshold; } else { double range = m_SensibleMaximumThreshold - m_SensibleMinimumThreshold; m_LowerThreshold = m_SensibleMinimumThreshold + range / 3.0; m_UpperThreshold = m_SensibleMinimumThreshold + 2 * range / 3.0; } bool isFloatImage = false; if ((referenceImage->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR) && (referenceImage->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT || referenceImage->GetPixelType().GetComponentType() == itk::IOComponentEnum::DOUBLE)) { isFloatImage = true; } IntervalBordersChanged.Send(m_SensibleMinimumThreshold, m_SensibleMaximumThreshold, isFloatImage); ThresholdingValuesChanged.Send(m_LowerThreshold, m_UpperThreshold); } } -void mitk::BinaryThresholdBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, Image* previewImage, TimeStepType timeStep) +void mitk::BinaryThresholdBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, LabelSetImage* previewImage, TimeStepType timeStep) { if (nullptr != inputAtTimeStep && nullptr != previewImage) { AccessByItk_n(inputAtTimeStep, ITKThresholding, (previewImage, timeStep)); } } template void mitk::BinaryThresholdBaseTool::ITKThresholding(const itk::Image* inputImage, Image* segmentation, unsigned int timeStep) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(inputImage); filter->SetLowerThreshold(m_LowerThreshold); filter->SetUpperThreshold(m_UpperThreshold); filter->SetInsideValue(this->GetUserDefinedActiveLabel()); filter->SetOutsideValue(0); filter->Update(); segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); } diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h index e7aa8fbddf..c283490bfc 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h @@ -1,77 +1,77 @@ /*============================================================================ 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 mitkBinaryThresholdBaseTool_h_Included #define mitkBinaryThresholdBaseTool_h_Included #include -#include +#include #include #include #include #include namespace mitk { /** \brief Base class for binary threshold tools. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ - class MITKSEGMENTATION_EXPORT BinaryThresholdBaseTool : public AutoSegmentationWithPreviewTool + class MITKSEGMENTATION_EXPORT BinaryThresholdBaseTool : public SegWithPreviewTool { public: Message3 IntervalBordersChanged; Message2 ThresholdingValuesChanged; - mitkClassMacro(BinaryThresholdBaseTool, AutoSegmentationWithPreviewTool); + mitkClassMacro(BinaryThresholdBaseTool, SegWithPreviewTool); virtual void SetThresholdValues(double lower, double upper); protected: BinaryThresholdBaseTool(); // purposely hidden ~BinaryThresholdBaseTool() override; itkSetMacro(LockedUpperThreshold, bool); itkGetMacro(LockedUpperThreshold, bool); itkBooleanMacro(LockedUpperThreshold); itkGetMacro(SensibleMinimumThreshold, ScalarType); itkGetMacro(SensibleMaximumThreshold, ScalarType); void InitiateToolByInput() override; - void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) override; + void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override; template void ITKThresholding(const itk::Image* inputImage, Image* segmentation, unsigned int timeStep); private: ScalarType m_SensibleMinimumThreshold; ScalarType m_SensibleMaximumThreshold; ScalarType m_LowerThreshold; ScalarType m_UpperThreshold; /** Indicates if the tool should behave like a single threshold tool (true) or like a upper/lower threshold tool (false)*/ bool m_LockedUpperThreshold = false; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp b/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp index ec02f6a346..a7ce47fcec 100644 --- a/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp +++ b/Modules/Segmentation/Interactions/mitkOtsuTool3D.cpp @@ -1,86 +1,87 @@ /*============================================================================ 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 "mitkOtsuSegmentationFilter.h" // us #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, OtsuTool3D, "Otsu Segmentation"); } void mitk::OtsuTool3D::Activated() { Superclass::Activated(); m_NumberOfBins = 128; m_NumberOfRegions = 2; m_UseValley = false; + this->SetLabelTransferMode(LabelTransferMode::AllLabels); } 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"; } -mitk::LabelSetImage::Pointer mitk::OtsuTool3D::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/) +void mitk::OtsuTool3D::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, LabelSetImage* previewImage, TimeStepType timeStep) { int numberOfThresholds = m_NumberOfRegions - 1; 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)"; } auto otsuResultImage = mitk::LabelSetImage::New(); otsuResultImage->InitializeByLabeledImage(otsuFilter->GetOutput()); - return otsuResultImage; + TransferLabelSetImageContent(otsuResultImage, previewImage, timeStep); } 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 3703b237a6..800d2f819f 100644 --- a/Modules/Segmentation/Interactions/mitkOtsuTool3D.h +++ b/Modules/Segmentation/Interactions/mitkOtsuTool3D.h @@ -1,64 +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 "mitkAutoMLSegmentationWithPreviewTool.h" +#include "mitkSegWithPreviewTool.h" #include namespace us { class ModuleResource; } namespace mitk { class Image; - class MITKSEGMENTATION_EXPORT OtsuTool3D : public AutoMLSegmentationWithPreviewTool + class MITKSEGMENTATION_EXPORT OtsuTool3D : public SegWithPreviewTool { public: - mitkClassMacro(OtsuTool3D, AutoMLSegmentationWithPreviewTool); + mitkClassMacro(OtsuTool3D, SegWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char *GetName() const override; const char **GetXPM() const override; us::ModuleResource GetIconResource() const override; void Activated() 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); /**Returns the number of max bins based on the current input image.*/ unsigned int GetMaxNumberOfBins() const; protected: OtsuTool3D() = default; ~OtsuTool3D() = default; - LabelSetImage::Pointer ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType timeStep) override; + void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override; unsigned int m_NumberOfBins = 128; unsigned int m_NumberOfRegions = 2; bool m_UseValley = false; }; // class } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp index b45438f6e3..6f94213db6 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp @@ -1,276 +1,276 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPickingTool.h" #include "mitkProperties.h" #include "mitkToolManager.h" #include "mitkInteractionPositionEvent.h" // us #include #include #include #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkImageTimeSelector.h" #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PickingTool, "PickingTool"); } -mitk::PickingTool::PickingTool() : AutoSegmentationWithPreviewTool(false, "PressMoveReleaseAndPointSetting") +mitk::PickingTool::PickingTool() : SegWithPreviewTool(false, "PressMoveReleaseAndPointSetting") { this->ResetsToEmptyPreviewOn(); } mitk::PickingTool::~PickingTool() { } bool mitk::PickingTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const { if (!Superclass::CanHandle(referenceData,workingData)) return false; auto* image = dynamic_cast(referenceData); if (image == nullptr) return false; return true; } const char **mitk::PickingTool::GetXPM() const { return nullptr; } const char *mitk::PickingTool::GetName() const { return "Picking"; } us::ModuleResource mitk::PickingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Pick_48x48.png"); return resource; } void mitk::PickingTool::Activated() { Superclass::Activated(); m_PointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->SetData(m_PointSet); m_PointSetNode->SetName(std::string(this->GetName()) + "_PointSet"); m_PointSetNode->SetBoolProperty("helper object", true); m_PointSetNode->SetColor(0.0, 1.0, 0.0); m_PointSetNode->SetVisibility(true); this->GetDataStorage()->Add(m_PointSetNode, this->GetToolManager()->GetWorkingData(0)); } void mitk::PickingTool::Deactivated() { this->ClearSeeds(); // remove from data storage and disable interaction GetDataStorage()->Remove(m_PointSetNode); m_PointSetNode = nullptr; m_PointSet = nullptr; Superclass::Deactivated(); } void mitk::PickingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } void mitk::PickingTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) { if (!this->IsUpdating() && m_PointSet.IsNotNull()) { const auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { m_PointSet->InsertPoint(m_PointSet->GetSize(), positionEvent->GetPositionInWorld()); this->UpdatePreview(); } } } void mitk::PickingTool::OnDelete(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { if (!this->IsUpdating() && m_PointSet.IsNotNull()) { // delete last seed point if (this->m_PointSet->GetSize() > 0) { m_PointSet->RemovePointAtEnd(0); this->UpdatePreview(); } } } void mitk::PickingTool::ClearPicks() { this->ClearSeeds(); this->UpdatePreview(); } bool mitk::PickingTool::HasPicks() const { return this->m_PointSet.IsNotNull() && this->m_PointSet->GetSize()>0; } void mitk::PickingTool::ClearSeeds() { if (this->m_PointSet.IsNotNull()) { // renew pointset this->m_PointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); this->m_PointSetNode->SetData(this->m_PointSet); } } template void DoITKRegionGrowing(const itk::Image* oldSegImage, mitk::Image* segmentation, const mitk::PointSet* seedPoints, unsigned int timeStep, const mitk::BaseGeometry* inputGeometry, const mitk::Label::PixelType outputValue, const mitk::Label::PixelType backgroundValue, bool& emptyTimeStep) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; using IndexMapType = std::map < mitk::Label::PixelType, std::vector >; IndexMapType indexMap; // convert world coordinates to image indices for (auto pos = seedPoints->Begin(); pos != seedPoints->End(); ++pos) { IndexType seedIndex; inputGeometry->WorldToIndex(pos->Value(), seedIndex); const auto selectedLabel = oldSegImage->GetPixel(seedIndex); if (selectedLabel != backgroundValue) { indexMap[selectedLabel].push_back(seedIndex); } } typename OutputImageType::Pointer itkResultImage; try { bool first = true; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); regionGrower->SetInput(oldSegImage); regionGrower->SetReplaceValue(outputValue); for (const auto& [label, indeces] : indexMap) { // perform region growing in desired segmented region regionGrower->ClearSeeds(); for (const auto& index : indeces) { regionGrower->AddSeed(index); } regionGrower->SetLower(label); regionGrower->SetUpper(label); regionGrower->Update(); if (first) { itkResultImage = regionGrower->GetOutput(); } else { typename itk::OrImageFilter::Pointer orFilter = itk::OrImageFilter::New(); orFilter->SetInput1(regionGrower->GetOutput()); orFilter->SetInput2(itkResultImage); orFilter->Update(); itkResultImage = orFilter->GetOutput(); } first = false; itkResultImage->DisconnectPipeline(); } } catch (const itk::ExceptionObject&) { return; // can't work } catch (...) { return; } if (itkResultImage.IsNotNull()) { segmentation->SetVolume((void*)(itkResultImage->GetPixelContainer()->GetBufferPointer()),timeStep); } emptyTimeStep = itkResultImage.IsNull(); } -void mitk::PickingTool::DoUpdatePreview(const Image* /*inputAtTimeStep*/, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) +void mitk::PickingTool::DoUpdatePreview(const Image* /*inputAtTimeStep*/, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) { if (nullptr != oldSegAtTimeStep && nullptr != previewImage && m_PointSet.IsNotNull()) { bool emptyTimeStep = true; if (this->HasPicks()) { Label::PixelType backgroundValue = 0; auto labelSetImage = dynamic_cast(oldSegAtTimeStep); if (nullptr != labelSetImage) { backgroundValue = labelSetImage->GetExteriorLabel()->GetValue(); } AccessFixedDimensionByItk_n(oldSegAtTimeStep, DoITKRegionGrowing, 3, (previewImage, this->m_PointSet, timeStep, oldSegAtTimeStep->GetGeometry(), this->GetUserDefinedActiveLabel(), backgroundValue, emptyTimeStep)); } if (emptyTimeStep) { this->ResetPreviewContentAtTimeStep(timeStep); } } } diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.h b/Modules/Segmentation/Interactions/mitkPickingTool.h index dd092dcf28..c129eb6087 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.h +++ b/Modules/Segmentation/Interactions/mitkPickingTool.h @@ -1,85 +1,85 @@ /*============================================================================ 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 mitkPickingTool_h_Included #define mitkPickingTool_h_Included -#include "mitkAutoSegmentationWithPreviewTool.h" +#include "mitkSegWithPreviewTool.h" #include "mitkPointSet.h" #include namespace us { class ModuleResource; } namespace mitk { /** \brief Extracts a single region from a segmentation image and creates a new image with same geometry of the input image. The region is extracted in 3D space. This is done by performing region growing within the desired region. Use shift click to add the seed point. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ - class MITKSEGMENTATION_EXPORT PickingTool : public AutoSegmentationWithPreviewTool + class MITKSEGMENTATION_EXPORT PickingTool : public SegWithPreviewTool { public: - mitkClassMacro(PickingTool, AutoSegmentationWithPreviewTool); + mitkClassMacro(PickingTool, SegWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; const char *GetName() const override; us::ModuleResource GetIconResource() const override; bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; void Activated() override; void Deactivated() override; /**Clears all picks and updates the preview.*/ void ClearPicks(); bool HasPicks() const; protected: PickingTool(); // purposely hidden ~PickingTool() override; void ConnectActionsAndFunctions() override; /// \brief Add point action of StateMachine pattern virtual void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent); /// \brief Delete action of StateMachine pattern virtual void OnDelete(StateMachineAction*, InteractionEvent* interactionEvent); /// \brief Clear all seed points. void ClearSeeds(); - void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) override; + void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override; // seed point PointSet::Pointer m_PointSet; DataNode::Pointer m_PointSetNode; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp similarity index 69% rename from Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp rename to Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp index 2d9d777380..0a5b9717bf 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp @@ -1,596 +1,732 @@ /*============================================================================ 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 "mitkSegWithPreviewTool.h" #include "mitkToolManager.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkLabelSetImage.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkNodePredicateGeometry.h" #include "mitkSegTool2D.h" -mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews): m_LazyDynamicPreviews(lazyDynamicPreviews) +mitk::SegWithPreviewTool::SegWithPreviewTool(bool lazyDynamicPreviews): Tool("dummy"), m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = ToolCommand::New(); } -mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : AutoSegmentationTool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews) +mitk::SegWithPreviewTool::SegWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = ToolCommand::New(); } -mitk::AutoSegmentationWithPreviewTool::~AutoSegmentationWithPreviewTool() +mitk::SegWithPreviewTool::~SegWithPreviewTool() { } -void mitk::AutoSegmentationWithPreviewTool::SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle) +void mitk::SegWithPreviewTool::SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle) { m_MergeStyle = mergeStyle; } -void mitk::AutoSegmentationWithPreviewTool::SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle) +void mitk::SegWithPreviewTool::SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle) { m_OverwriteStyle = overwriteStyle; } +void mitk::SegWithPreviewTool::SetLabelTransferMode(LabelTransferMode LabelTransferMode) +{ + m_LabelTransferMode = LabelTransferMode; +} + +void mitk::SegWithPreviewTool::SetSelectedLabels(const SelectedLabelVectorType& labelsToTransfer) +{ + m_SelectedLabels = labelsToTransfer; +} -bool mitk::AutoSegmentationWithPreviewTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const +bool mitk::SegWithPreviewTool::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() +void mitk::SegWithPreviewTool::Activated() { Superclass::Activated(); this->GetToolManager()->RoiDataChanged += - MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnRoiDataChanged); + MessageDelegate(this, &SegWithPreviewTool::OnRoiDataChanged); this->GetToolManager()->SelectedTimePointChanged += - MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnTimePointChanged); + MessageDelegate(this, &SegWithPreviewTool::OnTimePointChanged); m_ReferenceDataNode = this->GetToolManager()->GetReferenceData(0); m_SegmentationInputNode = m_ReferenceDataNode; m_LastTimePointOfUpdate = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (m_PreviewSegmentationNode.IsNull()) { m_PreviewSegmentationNode = DataNode::New(); m_PreviewSegmentationNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); m_PreviewSegmentationNode->SetProperty("name", StringProperty::New(std::string(this->GetName())+" preview")); m_PreviewSegmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); m_PreviewSegmentationNode->SetProperty("binary", BoolProperty::New(true)); m_PreviewSegmentationNode->SetProperty("helper object", BoolProperty::New(true)); } if (m_SegmentationInputNode.IsNotNull()) { this->ResetPreviewNode(); this->InitiateToolByInput(); } else { this->GetToolManager()->ActivateTool(-1); } } -void mitk::AutoSegmentationWithPreviewTool::Deactivated() +void mitk::SegWithPreviewTool::Deactivated() { this->GetToolManager()->RoiDataChanged -= - MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnRoiDataChanged); + MessageDelegate(this, &SegWithPreviewTool::OnRoiDataChanged); this->GetToolManager()->SelectedTimePointChanged -= - MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnTimePointChanged); + MessageDelegate(this, &SegWithPreviewTool::OnTimePointChanged); m_SegmentationInputNode = nullptr; m_ReferenceDataNode = nullptr; m_WorkingPlaneGeometry = nullptr; try { if (DataStorage *storage = this->GetToolManager()->GetDataStorage()) { storage->Remove(m_PreviewSegmentationNode); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (...) { // don't care } if (m_PreviewSegmentationNode.IsNotNull()) { m_PreviewSegmentationNode->SetData(nullptr); } Superclass::Deactivated(); } -void mitk::AutoSegmentationWithPreviewTool::ConfirmSegmentation() +void mitk::SegWithPreviewTool::ConfirmSegmentation() { bool labelChanged = this->EnsureUpToDateUserDefinedActiveLabel(); if ((m_LazyDynamicPreviews && m_CreateAllTimeSteps) || labelChanged) { // The tool should create all time steps but is currently in lazy mode, // thus ensure that a preview for all time steps is available. this->UpdatePreview(true); } - CreateResultSegmentationFromPreview(); RenderingManager::GetInstance()->RequestUpdateAll(); if (!m_KeepActiveAfterAccept) { this->GetToolManager()->ActivateTool(-1); } } -void mitk::AutoSegmentationWithPreviewTool::InitiateToolByInput() +void mitk::SegWithPreviewTool::InitiateToolByInput() { //default implementation does nothing. //implement in derived classes to change behavior } -mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentation() +mitk::LabelSetImage* mitk::SegWithPreviewTool::GetPreviewSegmentation() +{ + if (m_PreviewSegmentationNode.IsNull()) + { + return nullptr; + } + + return dynamic_cast(m_PreviewSegmentationNode->GetData()); +} + +const mitk::LabelSetImage* mitk::SegWithPreviewTool::GetPreviewSegmentation() const { if (m_PreviewSegmentationNode.IsNull()) { return nullptr; } - return dynamic_cast(m_PreviewSegmentationNode->GetData()); + return dynamic_cast(m_PreviewSegmentationNode->GetData()); } -mitk::DataNode* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentationNode() +mitk::DataNode* mitk::SegWithPreviewTool::GetPreviewSegmentationNode() { return m_PreviewSegmentationNode; } -const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetSegmentationInput() const +const mitk::Image* mitk::SegWithPreviewTool::GetSegmentationInput() const { if (m_SegmentationInputNode.IsNull()) { return nullptr; } return dynamic_cast(m_SegmentationInputNode->GetData()); } -const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetReferenceData() const +const mitk::Image* mitk::SegWithPreviewTool::GetReferenceData() const { if (m_ReferenceDataNode.IsNull()) { return nullptr; } return dynamic_cast(m_ReferenceDataNode->GetData()); } template void ClearBufferProcessing(ImageType* itkImage) { itkImage->FillBuffer(0); } -void mitk::AutoSegmentationWithPreviewTool::ResetPreviewContentAtTimeStep(unsigned int timeStep) +void mitk::SegWithPreviewTool::ResetPreviewContentAtTimeStep(unsigned int timeStep) { auto previewImage = GetImageByTimeStep(this->GetPreviewSegmentation(), timeStep); if (nullptr != previewImage) { AccessByItk(previewImage, ClearBufferProcessing); } } -void mitk::AutoSegmentationWithPreviewTool::ResetPreviewContent() +void mitk::SegWithPreviewTool::ResetPreviewContent() { auto previewImage = this->GetPreviewSegmentation(); if (nullptr != previewImage) { auto castedPreviewImage = dynamic_cast(previewImage); if (nullptr == castedPreviewImage) mitkThrow() << "Application is on wrong state / invalid tool implementation. Preview image should always be of type LabelSetImage now."; castedPreviewImage->ClearBuffer(); } } -void mitk::AutoSegmentationWithPreviewTool::ResetPreviewNode() +void mitk::SegWithPreviewTool::ResetPreviewNode() { if (m_IsUpdating) { mitkThrow() << "Used tool is implemented incorrectly. ResetPreviewNode is called while preview update is ongoing. Check implementation!"; } itk::RGBPixel previewColor; previewColor[0] = 0.0f; previewColor[1] = 1.0f; previewColor[2] = 0.0f; const auto image = this->GetSegmentationInput(); if (nullptr != image) { LabelSetImage::ConstPointer workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); if (workingImage.IsNotNull()) { auto newPreviewImage = workingImage->Clone(); if (this->GetResetsToEmptyPreview()) { newPreviewImage->ClearBuffer(); } if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); // Let's paint the feedback node green... auto* activeLayer = newPreviewImage->GetActiveLabelSet(); auto* activeLabel = activeLayer->GetActiveLabel(); activeLabel->SetColor(previewColor); activeLayer->UpdateLookupTable(activeLabel->GetValue()); activeLabel->SetVisible(true); } else { Image::ConstPointer workingImageBin = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); if (workingImageBin.IsNotNull()) { Image::Pointer newPreviewImage; if (this->GetResetsToEmptyPreview()) { newPreviewImage = Image::New(); newPreviewImage->Initialize(workingImageBin); } else { auto newPreviewImage = workingImageBin->Clone(); } if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); } else { mitkThrow() << "Tool is an invalid state. Cannot setup preview node. Working data is an unsupported class and should have not been accepted by CanHandle()."; } } m_PreviewSegmentationNode->SetColor(previewColor); m_PreviewSegmentationNode->SetOpacity(0.5); int layer(50); m_ReferenceDataNode->GetIntProperty("layer", layer); m_PreviewSegmentationNode->SetIntProperty("layer", layer + 1); if (DataStorage *ds = this->GetToolManager()->GetDataStorage()) { if (!ds->Exists(m_PreviewSegmentationNode)) ds->Add(m_PreviewSegmentationNode, m_ReferenceDataNode); } } } -void mitk::AutoSegmentationWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep) +mitk::SegWithPreviewTool::LabelMappingType mitk::SegWithPreviewTool::GetLabelMapping() const +{ + LabelMappingType labelMapping = { { this->GetUserDefinedActiveLabel(),this->GetUserDefinedActiveLabel() } }; + if (LabelTransferMode::SelectedLabels == this->m_LabelTransferMode) + { + labelMapping.clear(); + for (auto label : this->m_SelectedLabels) + { + labelMapping.push_back({ label, label }); + } + } + else if (LabelTransferMode::AllLabels == this->m_LabelTransferMode) + { + labelMapping.clear(); + const auto labelSet = this->GetPreviewSegmentation()->GetActiveLabelSet(); + for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelSet->IteratorConstEnd(); ++labelIter) + { + labelMapping.push_back({ labelIter->second->GetValue(),labelIter->second->GetValue() }); + } + } + + return labelMapping; +} + +void mitk::SegWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep) { try { Image::ConstPointer sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep); if (sourceImageAtTimeStep->GetPixelType() != destinationImage->GetPixelType()) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same pixel type. " << "Source pixel type: " << sourceImage->GetPixelType().GetTypeAsString() << "; destination pixel type: " << destinationImage->GetPixelType().GetTypeAsString(); } if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, false)) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry."; } if (nullptr != this->GetWorkingPlaneGeometry()) { auto sourceSlice = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), sourceImage, timeStep); SegTool2D::WriteBackSegmentationResult(this->GetTargetSegmentationNode(), m_WorkingPlaneGeometry, sourceSlice, timeStep); } else { //take care of the full segmentation volume auto sourceLSImage = dynamic_cast(sourceImage); auto destLSImage = dynamic_cast(destinationImage); - TransferLabelContent(sourceLSImage, destLSImage, { {this->GetUserDefinedActiveLabel(),this->GetUserDefinedActiveLabel()} }, m_MergeStyle, - m_OverwriteStyle, timeStep); + auto labelMapping = this->GetLabelMapping(); + TransferLabelContent(sourceLSImage, destLSImage, labelMapping, m_MergeStyle, m_OverwriteStyle, timeStep); } } catch (...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); throw; } } -void mitk::AutoSegmentationWithPreviewTool::CreateResultSegmentationFromPreview() +void mitk::SegWithPreviewTool::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 = 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." + mitkThrow() << "Cannot confirm/transfer segmentation. Internal tool state is invalid." << " Preview segmentation and segmentation result image have different time geometries."; } + this->TransferPrepare(); if (m_CreateAllTimeSteps) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } } else { const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } // since we are maybe working on a smaller image, pad it to the size of the original image if (m_ReferenceDataNode.GetPointer() != m_SegmentationInputNode.GetPointer()) { PadImageFilter::Pointer padFilter = PadImageFilter::New(); padFilter->SetInput(0, resultSegmentation); padFilter->SetInput(1, dynamic_cast(m_ReferenceDataNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); resultSegmentationNode->SetData(padFilter->GetOutput()); } - if (m_OverwriteExistingSegmentation) - { //if we overwrite the segmentation (and not just store it as a new result - //in the data storage) we update also the tool manager state. - this->GetToolManager()->SetWorkingData(resultSegmentationNode); - this->GetToolManager()->GetWorkingData(0)->Modified(); - } this->EnsureTargetSegmentationNodeInDataStorage(); } } } -void mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged() +void mitk::SegWithPreviewTool::OnRoiDataChanged() { DataNode::ConstPointer node = this->GetToolManager()->GetRoiData(0); if (node.IsNotNull()) { MaskAndCutRoiImageFilter::Pointer roiFilter = MaskAndCutRoiImageFilter::New(); Image::Pointer image = dynamic_cast(m_SegmentationInputNode->GetData()); if (image.IsNull()) return; roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); DataNode::Pointer tmpNode = DataNode::New(); tmpNode->SetData(roiFilter->GetOutput()); m_SegmentationInputNode = tmpNode; } else m_SegmentationInputNode = m_ReferenceDataNode; this->ResetPreviewNode(); this->InitiateToolByInput(); this->UpdatePreview(); } -void mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged() +void mitk::SegWithPreviewTool::OnTimePointChanged() { if (m_IsTimePointChangeAware && m_PreviewSegmentationNode.IsNotNull() && m_SegmentationInputNode.IsNotNull()) { const auto timePoint = 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(); } } } -bool mitk::AutoSegmentationWithPreviewTool::EnsureUpToDateUserDefinedActiveLabel() +bool mitk::SegWithPreviewTool::EnsureUpToDateUserDefinedActiveLabel() { bool labelChanged = true; const auto workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); if (const auto& labelSetImage = dynamic_cast(workingImage)) { // this is a fix for T28131 / T28986, which should be refactored if T28524 is being worked on auto newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue(); labelChanged = newLabel != m_UserDefinedActiveLabel; m_UserDefinedActiveLabel = newLabel; } else { m_UserDefinedActiveLabel = 1; labelChanged = false; } return labelChanged; } -void mitk::AutoSegmentationWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting) +void mitk::SegWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting) { const auto inputImage = this->GetSegmentationInput(); auto previewImage = this->GetPreviewSegmentation(); int progress_steps = 200; const auto workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); this->EnsureUpToDateUserDefinedActiveLabel(); this->CurrentlyBusy.Send(true); m_IsUpdating = true; this->UpdatePrepare(); const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { if (nullptr != inputImage && nullptr != previewImage) { m_ProgressCommand->AddStepsToDo(progress_steps); if (previewImage->GetTimeSteps() > 1 && (ignoreLazyPreviewSetting || !m_LazyDynamicPreviews)) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { Image::ConstPointer feedBackImage; Image::ConstPointer currentSegImage; auto previewTimePoint = previewImage->GetTimeGeometry()->TimeStepToTimePoint(timeStep); auto inputTimeStep = inputImage->GetTimeGeometry()->TimePointToTimeStep(previewTimePoint); if (nullptr != this->GetWorkingPlaneGeometry()) { //only extract a specific slice defined by the working plane as feedback image. feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, inputTimeStep); currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, previewTimePoint); } else { //work on the whole feedback image feedBackImage = this->GetImageByTimeStep(inputImage, inputTimeStep); currentSegImage = this->GetImageByTimePoint(workingImage, previewTimePoint); } this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep); } } else { Image::ConstPointer feedBackImage; Image::ConstPointer currentSegImage; if (nullptr != this->GetWorkingPlaneGeometry()) { feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint); currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, timePoint); } else { feedBackImage = this->GetImageByTimePoint(inputImage, timePoint); currentSegImage = this->GetImageByTimePoint(workingImage, timePoint); } auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep); } RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (itk::ExceptionObject & excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); m_ProgressCommand->SetProgress(progress_steps); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); } catch (...) { m_ProgressCommand->SetProgress(progress_steps); m_IsUpdating = false; CurrentlyBusy.Send(false); throw; } this->UpdateCleanUp(); m_LastTimePointOfUpdate = timePoint; m_ProgressCommand->SetProgress(progress_steps); m_IsUpdating = false; CurrentlyBusy.Send(false); } -bool mitk::AutoSegmentationWithPreviewTool::IsUpdating() const +bool mitk::SegWithPreviewTool::IsUpdating() const { return m_IsUpdating; } -void mitk::AutoSegmentationWithPreviewTool::UpdatePrepare() +void mitk::SegWithPreviewTool::UpdatePrepare() { // default implementation does nothing //reimplement in derived classes for special behavior } -void mitk::AutoSegmentationWithPreviewTool::UpdateCleanUp() +void mitk::SegWithPreviewTool::UpdateCleanUp() { // default implementation does nothing //reimplement in derived classes for special behavior } -mitk::TimePointType mitk::AutoSegmentationWithPreviewTool::GetLastTimePointOfUpdate() const +void mitk::SegWithPreviewTool::TransferLabelInformation(LabelMappingType& labelMapping, + const mitk::LabelSetImage* source, mitk::LabelSetImage* target) +{ + for (const auto& [sourceLabel, targetLabel] : labelMapping) + { + if (!target->ExistLabel(targetLabel, target->GetActiveLayer())) + { + if (!source->ExistLabel(sourceLabel, source->GetActiveLayer())) + { + mitkThrow() << "Cannot prepare segmentation for preview transfer. Preview seems invalid as label is missing. Missing label: " << sourceLabel; + } + + auto clonedLabel = source->GetLabel(sourceLabel, source->GetActiveLayer())->Clone(); + clonedLabel->SetValue(targetLabel); + target->GetActiveLabelSet()->AddLabel(clonedLabel); + } + } +} + +void mitk::SegWithPreviewTool::TransferPrepare() +{ + auto labelMapping = this->GetLabelMapping(); + + DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode(); + + if (resultSegmentationNode.IsNotNull()) + { + auto resultSegmentation = dynamic_cast(resultSegmentationNode->GetData()); + + if (nullptr == resultSegmentation) + { + mitkThrow() << "Cannot prepare segmentation for preview transfer. Tool is in invalid state as segmentation is not existing or of right type"; + } + + auto preview = this->GetPreviewSegmentation(); + TransferLabelInformation(labelMapping, preview, resultSegmentation); + } +} + +mitk::TimePointType mitk::SegWithPreviewTool::GetLastTimePointOfUpdate() const { return m_LastTimePointOfUpdate; } + +const char* mitk::SegWithPreviewTool::GetGroup() const +{ + return "autoSegmentation"; +} + +mitk::Image::ConstPointer mitk::SegWithPreviewTool::GetImageByTimeStep(const mitk::Image* image, TimeStepType timestep) +{ + return SelectImageByTimeStep(image, timestep); +} + +mitk::Image::Pointer mitk::SegWithPreviewTool::GetImageByTimeStep(mitk::Image* image, TimeStepType timestep) +{ + return SelectImageByTimeStep(image, timestep); +} + +mitk::Image::ConstPointer mitk::SegWithPreviewTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint) +{ + return SelectImageByTimeStep(image, timePoint); +} + +void mitk::SegWithPreviewTool::EnsureTargetSegmentationNodeInDataStorage() const +{ + auto targetNode = this->GetTargetSegmentationNode(); + auto dataStorage = this->GetToolManager()->GetDataStorage(); + if (!dataStorage->Exists(targetNode)) + { + dataStorage->Add(targetNode, this->GetToolManager()->GetReferenceData(0)); + } +} + +std::string mitk::SegWithPreviewTool::GetCurrentSegmentationName() +{ + auto workingData = this->GetToolManager()->GetWorkingData(0); + + return nullptr != workingData + ? workingData->GetName() + : ""; +} + + +mitk::DataNode* mitk::SegWithPreviewTool::GetTargetSegmentationNode() const +{ + return this->GetToolManager()->GetWorkingData(0); +} + +void mitk::SegWithPreviewTool::TransferLabelSetImageContent(const LabelSetImage* source, LabelSetImage* target, TimeStepType timeStep) +{ + mitk::ImageReadAccessor newMitkImgAcc(source); + + LabelMappingType labelMapping; + const auto labelSet = source->GetActiveLabelSet(); + for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelSet->IteratorConstEnd(); ++labelIter) + { + labelMapping.push_back({ labelIter->second->GetValue(),labelIter->second->GetValue() }); + } + TransferLabelInformation(labelMapping, source, target); + + target->SetVolume(newMitkImgAcc.GetData(), timeStep); +} diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h similarity index 70% rename from Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h rename to Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h index 09ec1d6634..b9462aa25e 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h +++ b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h @@ -1,225 +1,287 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkAutoSegmentationWithPreviewTool_h_Included -#define mitkAutoSegmentationWithPreviewTool_h_Included +#ifndef mitkSegWithPreviewTool_h_Included +#define mitkSegWithPreviewTool_h_Included -#include "mitkAutoSegmentationTool.h" +#include "mitkTool.h" #include "mitkCommon.h" #include "mitkDataNode.h" #include "mitkToolCommand.h" #include namespace mitk { /** \brief Base class for any auto segmentation tool that provides a preview of the new segmentation. This tool class implements a lot basic logic to handle auto segmentation tools with preview, Time point and ROI support. Derived classes will ask to update the segmentation preview if needed (e.g. because the ROI or the current time point has changed) or because derived tools indicated the need to update themselves. This class also takes care to properly transfer a confirmed preview into the segementation result. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ - class MITKSEGMENTATION_EXPORT AutoSegmentationWithPreviewTool : public AutoSegmentationTool + class MITKSEGMENTATION_EXPORT SegWithPreviewTool : public Tool { public: - mitkClassMacro(AutoSegmentationWithPreviewTool, AutoSegmentationTool); + mitkClassMacro(SegWithPreviewTool, Tool); void Activated() override; void Deactivated() override; void ConfirmSegmentation(); itkSetMacro(CreateAllTimeSteps, bool); itkGetMacro(CreateAllTimeSteps, bool); itkBooleanMacro(CreateAllTimeSteps); itkSetMacro(KeepActiveAfterAccept, bool); itkGetMacro(KeepActiveAfterAccept, bool); itkBooleanMacro(KeepActiveAfterAccept); itkSetMacro(IsTimePointChangeAware, bool); itkGetMacro(IsTimePointChangeAware, bool); itkBooleanMacro(IsTimePointChangeAware); itkSetMacro(ResetsToEmptyPreview, bool); itkGetMacro(ResetsToEmptyPreview, bool); itkBooleanMacro(ResetsToEmptyPreview); /*itk macro was not used on purpose, to aviod the change of mtime.*/ void SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle); itkGetMacro(MergeStyle, MultiLabelSegmentation::MergeStyle); /*itk macro was not used on purpose, to aviod the change of mtime.*/ void SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle); itkGetMacro(OverwriteStyle, MultiLabelSegmentation::OverwriteStyle); + enum class LabelTransferMode + { + ActiveLabel, //Only the active label will be transfered from preview to segmentation. + SelectedLabels, //The labels defined as selected labels will be transfered. + AllLabels //Transfer all labels of the preview + }; + /*itk macro was not used on purpose, to aviod the change of mtime.*/ + void SetLabelTransferMode(LabelTransferMode LabelTransferMode); + itkGetMacro(LabelTransferMode, LabelTransferMode); + + using SelectedLabelVectorType = std::vector; + /** Specifies the labels that should be transfered form preview to the working image, + if the segmentation is confirmed. The setting will be used, if LabelTransferMode is set to "SelectedLabels".*/ + void SetSelectedLabels(const SelectedLabelVectorType& labelsToTransfer); + itkGetMacro(SelectedLabels, SelectedLabelVectorType); + bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; /** Triggers the actualization of the preview * @param ignoreLazyPreviewSetting If set true UpdatePreview will always * generate the preview for all time steps. If set to false, UpdatePreview * will regard the setting specified by the constructor. * To define the update generation for time steps implement DoUpdatePreview. * To alter what should be done directly before or after the update of the preview, * reimplement UpdatePrepare() or UpdateCleanUp().*/ void UpdatePreview(bool ignoreLazyPreviewSetting = false); /** Indicate if currently UpdatePreview is triggered (true) or not (false).*/ bool IsUpdating() const; + /** + * @brief Gets the name of the currently selected segmentation node + * @return the name of the segmentation node or an empty string if + * none is selected + */ + std::string GetCurrentSegmentationName(); + + /** + * @brief Returns the currently selected segmentation node + * @return a mitk::DataNode which contains a segmentation image + */ + virtual DataNode* GetTargetSegmentationNode() const; + + /** Returns the image that contains the preview of the current segmentation. + * Returns null if the node is not set or does not contain an image.*/ + LabelSetImage* GetPreviewSegmentation(); + const LabelSetImage* GetPreviewSegmentation() const; + DataNode* GetPreviewSegmentationNode(); + protected: ToolCommand::Pointer m_ProgressCommand; + SegWithPreviewTool(bool lazyDynamicPreviews = false); // purposely hidden + SegWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden + + ~SegWithPreviewTool() override; + + const char* GetGroup() const override; + + /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ + static Image::ConstPointer GetImageByTimeStep(const Image* image, TimeStepType timestep); + /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ + static Image::Pointer GetImageByTimeStep(Image* image, TimeStepType timestep); + /** Helper that extracts the image for the passed time point, if the image has multiple time steps.*/ + static Image::ConstPointer GetImageByTimePoint(const Image* image, TimePointType timePoint); + + void EnsureTargetSegmentationNodeInDataStorage() const; + /** Member is always called if GetSegmentationInput() has changed * (e.g. because a new ROI was defined, or on activation) to give derived * classes the posibility to initiate their state accordingly. * Reimplement this function to implement special behavior. */ virtual void InitiateToolByInput(); /** This member function offers derived classes the possibility to alter what should happen directly before the update of the preview is performed. It is called by UpdatePreview. Default implementation does nothing.*/ virtual void UpdatePrepare(); /** This member function offers derived classes the possibility to alter what should happen directly after the update of the preview is performed. It is called by UpdatePreview. Default implementation does nothing.*/ virtual void UpdateCleanUp(); + /** This member function offers derived classes the possibility to alter what should + happen directly before the content of the preview is transfered to the segmentation, + when the segmentation is confirmed. It is called by CreateResultSegmentationFromPreview. + Default implementation ensure that all labels that will be transfered, exist in the + segmentation. If they are not existing before the transfer, the will be added by + cloning the label information of the preview.*/ + virtual void TransferPrepare(); + + using LabelMappingType = std::vector >; + static void TransferLabelInformation(LabelMappingType& labelMapping, + const mitk::LabelSetImage* source, mitk::LabelSetImage* target); + + /**Helper function that can be used to move the content of an LabelSetImage (the pixels of the active source layer and the labels). + This is e.g. helpfull if you generate an LabelSetImage content in DoUpdatePreview and you want to transfer it into the preview image.*/ + static void TransferLabelSetImageContent(const LabelSetImage* source, LabelSetImage* target, TimeStepType timeStep); + /** This function does the real work. Here the preview for a given * input image should be computed and stored in the also passed * preview image at the passed time step. * It also provides the current/old segmentation at the time point, * which can be used, if the preview depends on the the segmenation so far. */ - virtual void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) = 0; - - AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews = false); // purposely hidden - AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden - - ~AutoSegmentationWithPreviewTool() override; - - /** Returns the image that contains the preview of the current segmentation. - * Returns null if the node is not set or does not contain an image.*/ - Image* GetPreviewSegmentation(); - DataNode* GetPreviewSegmentationNode(); + virtual void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) = 0; /** Returns the input that should be used for any segmentation/preview or tool update. * It is either the data of ReferenceDataNode itself or a part of it defined by a ROI mask * provided by the tool manager. Derived classes should regard this as the relevant * input data for any processing. * Returns null if the node is not set or does not contain an image.*/ const Image* GetSegmentationInput() const; /** Returns the image that is provided by the ReferenceDataNode. * Returns null if the node is not set or does not contain an image.*/ const Image* GetReferenceData() const; /** Resets the preview node so it is empty and ready to be filled by the tool @remark Calling this function will generate a new preview image, and the old might be invalidated. Therefore this function should not be used within the scope of UpdatePreview (m_IsUpdating == true).*/ void ResetPreviewNode(); /** Resets the complete content of the preview image. The instance of the preview image and its settings * stay the same.*/ void ResetPreviewContent(); /** Resets only the image content of the specified timeStep of the preview image. If the preview image or the specified time step does not exist, nothing happens.*/ void ResetPreviewContentAtTimeStep(unsigned int timeStep); TimePointType GetLastTimePointOfUpdate() const; itkGetConstMacro(UserDefinedActiveLabel, Label::PixelType); itkSetObjectMacro(WorkingPlaneGeometry, PlaneGeometry); itkGetConstObjectMacro(WorkingPlaneGeometry, PlaneGeometry); private: void TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep); void CreateResultSegmentationFromPreview(); void OnRoiDataChanged(); void OnTimePointChanged(); /**Internal helper that ensures that the stored active label is up to date. This is a fix for T28131 / T28986. It should be refactored if T28524 is being worked on. On the long run, the active label will be communicated/set by the user/toolmanager as a state of the tool and the tool should react accordingly (like it does for other external state changes). @return indicates if the label has changed (true) or not. */ bool EnsureUpToDateUserDefinedActiveLabel(); + LabelMappingType GetLabelMapping() const; + /** Node that containes the preview data generated and managed by this class or derived ones.*/ DataNode::Pointer m_PreviewSegmentationNode; /** The reference data recieved from ToolManager::GetReferenceData when tool was activated.*/ DataNode::Pointer m_ReferenceDataNode; /** Node that containes the data that should be used as input for any auto segmentation. It might * be the same like m_ReferenceDataNode (if no ROI is set) or a sub region (if ROI is set).*/ DataNode::Pointer m_SegmentationInputNode; /** Indicates if Accepting the threshold should transfer/create the segmentations of all time steps (true) or only of the currently selected timepoint (false).*/ bool m_CreateAllTimeSteps = false; /** Indicates if the tool should kept active after accepting the segmentation or not.*/ bool m_KeepActiveAfterAccept = false; /** Relevant if the working data / preview image has multiple time steps (dynamic segmentations). * This flag has to be set by derived classes accordingly to there way to generate dynamic previews. * If LazyDynamicPreview is true, the tool generates only the preview for the current time step. * Therefore it always has to update the preview if current time point has changed and it has to (re)compute * all timeframes if ConfirmSegmentation() is called.*/ bool m_LazyDynamicPreviews = false; bool m_IsTimePointChangeAware = true; /** Controls if ResetPreviewNode generates an empty content (true) or clones the current segmentation (false).*/ bool m_ResetsToEmptyPreview = false; TimePointType m_LastTimePointOfUpdate = 0.; bool m_IsUpdating = false; Label::PixelType m_UserDefinedActiveLabel = 1; /** This variable indicates if for the tool a working plane geometry is defined. * If a working plane is defined the tool will only work an the slice of the input * and the segmentation. Thus only the relevant input slice will be passed to * DoUpdatePreview(...) and only the relevant slice of the preview will be transfered when * ConfirmSegmentation() is called.*/ PlaneGeometry::Pointer m_WorkingPlaneGeometry; /** This variable controles how the label pixel content of the preview should be transfered into the segmentation- For more details of the behavior see documentation of MultiLabelSegmentation::MergeStyle. */ MultiLabelSegmentation::MergeStyle m_MergeStyle = MultiLabelSegmentation::MergeStyle::Replace; /** This variable controles how the label pixel content of the preview should be transfered into the segmentation- For more details of the behavior see documentation of MultiLabelSegmentation::OverwriteStyle. */ MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = MultiLabelSegmentation::OverwriteStyle::RegardLocks; + + LabelTransferMode m_LabelTransferMode = LabelTransferMode::ActiveLabel; + SelectedLabelVectorType m_SelectedLabels = {}; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp index 99779403d0..332713e16c 100644 --- a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp +++ b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp @@ -1,327 +1,325 @@ /*============================================================================ 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 "mitknnUnetTool.h" #include "mitkIOUtil.h" #include "mitkProcessExecutor.h" #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, nnUNetTool, "nnUNet tool"); } mitk::nnUNetTool::nnUNetTool() { this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); } mitk::nnUNetTool::~nnUNetTool() { itksys::SystemTools::RemoveADirectory(this->GetMitkTempDir()); } void mitk::nnUNetTool::Activated() { Superclass::Activated(); + this->SetLabelTransferMode(LabelTransferMode::AllLabels); } void mitk::nnUNetTool::RenderOutputBuffer() { if (m_OutputBuffer != nullptr) { - Superclass::SetNodeProperties(m_OutputBuffer); try { if (nullptr != this->GetPreviewSegmentationNode()) { this->GetPreviewSegmentationNode()->SetVisibility(!this->GetSelectedLabels().empty()); } if (this->GetSelectedLabels().empty()) { this->ResetPreviewNode(); } } catch (const mitk::Exception &e) { MITK_INFO << e.GetDescription(); } } } void mitk::nnUNetTool::SetOutputBuffer(LabelSetImage::Pointer segmentation) { m_OutputBuffer = segmentation; } mitk::LabelSetImage::Pointer mitk::nnUNetTool::GetOutputBuffer() { return m_OutputBuffer; } void mitk::nnUNetTool::ClearOutputBuffer() { m_OutputBuffer = nullptr; } us::ModuleResource mitk::nnUNetTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI_48x48.png"); return resource; } const char **mitk::nnUNetTool::GetXPM() const { return nullptr; } const char *mitk::nnUNetTool::GetName() const { return "nnUNet"; } mitk::DataStorage *mitk::nnUNetTool::GetDataStorage() { return this->GetToolManager()->GetDataStorage(); } mitk::DataNode *mitk::nnUNetTool::GetRefNode() { return this->GetToolManager()->GetReferenceData(0); } namespace { void onPythonProcessEvent(itk::Object * /*pCaller*/, const itk::EventObject &e, void *) { std::string testCOUT; std::string testCERR; const auto *pEvent = dynamic_cast(&e); if (pEvent) { testCOUT = testCOUT + pEvent->GetOutput(); MITK_INFO << testCOUT; } const auto *pErrEvent = dynamic_cast(&e); if (pErrEvent) { testCERR = testCERR + pErrEvent->GetOutput(); MITK_ERROR << testCERR; } } } // namespace -mitk::LabelSetImage::Pointer mitk::nnUNetTool::ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType /*timeStep*/) +void mitk::nnUNetTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, LabelSetImage* previewImage, TimeStepType /*timeStep*/) { if (m_InputBuffer == inputAtTimeStep) { - return m_OutputBuffer; + return; } + std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath; ProcessExecutor::Pointer spExec = ProcessExecutor::New(); itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(&onPythonProcessEvent); spExec->AddObserver(ExternalProcessOutputEvent(), spCommand); ProcessExecutor::ArgumentListType args; inDir = IOUtil::CreateTemporaryDirectory("nnunet-in-XXXXXX", this->GetMitkTempDir()); std::ofstream tmpStream; inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, m_TEMPLATE_FILENAME, inDir + IOUtil::GetDirectorySeparator()); tmpStream.close(); std::size_t found = inputImagePath.find_last_of(IOUtil::GetDirectorySeparator()); std::string fileName = inputImagePath.substr(found + 1); std::string token = fileName.substr(0, fileName.find("_")); if (this->GetNoPip()) { scriptPath = this->GetnnUNetDirectory() + IOUtil::GetDirectorySeparator() + "nnunet" + IOUtil::GetDirectorySeparator() + "inference" + IOUtil::GetDirectorySeparator() + "predict_simple.py"; } try { if (this->GetMultiModal()) { const std::string fileFormat(".nii.gz"); const std::string fileNamePart("_000_000"); std::string outModalFile; size_t len = inDir.length() + 1 + token.length() + fileNamePart.length() + 1 + fileFormat.length(); outModalFile.reserve(len); // The 1(s) indicates a directory separator char and an underscore. for (size_t i = 0; i < m_OtherModalPaths.size(); ++i) { mitk::Image::ConstPointer modalImage = m_OtherModalPaths[i]; outModalFile.append(inDir); outModalFile.push_back(IOUtil::GetDirectorySeparator()); outModalFile.append(token); outModalFile.append(fileNamePart); outModalFile.append(std::to_string(i)); outModalFile.append(fileFormat); IOUtil::Save(modalImage.GetPointer(), outModalFile); outModalFile.clear(); } } else { IOUtil::Save(inputAtTimeStep, inputImagePath); } } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); - return nullptr; + return; } // Code calls external process std::string command = "nnUNet_predict"; if (this->GetNoPip()) { #ifdef _WIN32 command = "python"; #else command = "python3"; #endif } for (ModelParams &modelparam : m_ParamQ) { outDir = IOUtil::CreateTemporaryDirectory("nnunet-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; modelparam.outputDir = outDir; args.clear(); if (this->GetNoPip()) { args.push_back(scriptPath); } args.push_back("-i"); args.push_back(inDir); args.push_back("-o"); args.push_back(outDir); args.push_back("-t"); args.push_back(modelparam.task); if (modelparam.model.find("cascade") != std::string::npos) { args.push_back("-ctr"); } else { args.push_back("-tr"); } args.push_back(modelparam.trainer); args.push_back("-m"); args.push_back(modelparam.model); args.push_back("-p"); args.push_back(modelparam.planId); if (!modelparam.folds.empty()) { args.push_back("-f"); for (auto fold : modelparam.folds) { args.push_back(fold); } } args.push_back("--num_threads_nifti_save"); args.push_back("1"); // fixing to 1 if (!this->GetMirror()) { args.push_back("--disable_tta"); } if (!this->GetMixedPrecision()) { args.push_back("--disable_mixed_precision"); } if (this->GetEnsemble()) { args.push_back("--save_npz"); } try { std::string resultsFolderEnv = "RESULTS_FOLDER=" + this->GetModelDirectory(); itksys::SystemTools::PutEnv(resultsFolderEnv.c_str()); std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(this->GetGpuId()); itksys::SystemTools::PutEnv(cudaEnv.c_str()); spExec->Execute(this->GetPythonPath(), command, args); } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); - return nullptr; + return; } } if (this->GetEnsemble() && !this->GetPostProcessingJsonDirectory().empty()) { args.clear(); command = "nnUNet_ensemble"; outDir = IOUtil::CreateTemporaryDirectory("nnunet-ensemble-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; args.push_back("-f"); for (ModelParams &modelparam : m_ParamQ) { args.push_back(modelparam.outputDir); } args.push_back("-o"); args.push_back(outDir); if (!this->GetPostProcessingJsonDirectory().empty()) { args.push_back("-pp"); args.push_back(this->GetPostProcessingJsonDirectory()); } spExec->Execute(this->GetPythonPath(), command, args); } try { - LabelSetImage::Pointer resultImage = LabelSetImage::New(); Image::Pointer outputImage = IOUtil::Load(outputImagePath); - resultImage->InitializeByLabeledImage(outputImage); - resultImage->SetGeometry(inputAtTimeStep->GetGeometry()); + previewImage->InitializeByLabeledImage(outputImage); m_InputBuffer = inputAtTimeStep; - m_OutputBuffer = resultImage; - return resultImage; + m_OutputBuffer = previewImage; } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); - return nullptr; + return; } } diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.h b/Modules/Segmentation/Interactions/mitknnUnetTool.h index 7e0fcffd7a..320bfca130 100644 --- a/Modules/Segmentation/Interactions/mitknnUnetTool.h +++ b/Modules/Segmentation/Interactions/mitknnUnetTool.h @@ -1,213 +1,213 @@ /*============================================================================ 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 mitknnUnetTool_h_Included #define mitknnUnetTool_h_Included -#include "mitkAutoMLSegmentationWithPreviewTool.h" +#include "mitkSegWithPreviewTool.h" #include "mitkCommon.h" #include "mitkToolManager.h" #include #include #include #include namespace us { class ModuleResource; } namespace mitk { /** * @brief nnUNet parameter request object holding all model parameters for input. * Also holds output temporary directory path. */ struct ModelParams { std::string task; std::vector folds; std::string model; std::string trainer; std::string planId; std::string outputDir; std::string inputName; std::string timeStamp; size_t generateHash() const { std::string toHash; std::string foldsConcatenated = std::accumulate(folds.begin(), folds.end(), std::string("")); toHash += this->task; toHash += this->model; toHash += this->inputName; toHash += foldsConcatenated; toHash += this->timeStamp; size_t hashVal = std::hash{}(toHash); return hashVal; } }; /** \brief nnUNet segmentation tool. \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ - class MITKSEGMENTATION_EXPORT nnUNetTool : public AutoMLSegmentationWithPreviewTool + class MITKSEGMENTATION_EXPORT nnUNetTool : public SegWithPreviewTool { public: - mitkClassMacro(nnUNetTool, AutoMLSegmentationWithPreviewTool); + mitkClassMacro(nnUNetTool, SegWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; const char *GetName() const override; us::ModuleResource GetIconResource() const override; void Activated() override; itkSetMacro(nnUNetDirectory, std::string); itkGetConstMacro(nnUNetDirectory, std::string); itkSetMacro(ModelDirectory, std::string); itkGetConstMacro(ModelDirectory, std::string); itkSetMacro(PythonPath, std::string); itkGetConstMacro(PythonPath, std::string); itkSetMacro(MitkTempDir, std::string); itkGetConstMacro(MitkTempDir, std::string); itkSetMacro(PostProcessingJsonDirectory, std::string); itkGetConstMacro(PostProcessingJsonDirectory, std::string); itkSetMacro(MixedPrecision, bool); itkGetConstMacro(MixedPrecision, bool); itkBooleanMacro(MixedPrecision); itkSetMacro(Mirror, bool); itkGetConstMacro(Mirror, bool); itkBooleanMacro(Mirror); itkSetMacro(MultiModal, bool); itkGetConstMacro(MultiModal, bool); itkBooleanMacro(MultiModal); itkSetMacro(NoPip, bool); itkGetConstMacro(NoPip, bool); itkBooleanMacro(NoPip); itkSetMacro(Ensemble, bool); itkGetConstMacro(Ensemble, bool); itkBooleanMacro(Ensemble); itkSetMacro(Predict, bool); itkGetConstMacro(Predict, bool); itkBooleanMacro(Predict); itkSetMacro(GpuId, unsigned int); itkGetConstMacro(GpuId, unsigned int); /** * @brief vector of ModelParams. * Size > 1 only for ensemble prediction. */ std::vector m_ParamQ; /** * @brief Holds paths to other input image modalities. * */ std::vector m_OtherModalPaths; mitk::Image::ConstPointer m_InputBuffer; /** * @brief Renders the output LabelSetImage. * To called in the main thread. */ void RenderOutputBuffer(); /** * @brief Get the Output Buffer object * * @return LabelSetImage::Pointer */ LabelSetImage::Pointer GetOutputBuffer(); /** * @brief Sets the outputBuffer to nullptr * */ void ClearOutputBuffer(); /** * @brief Returns the DataStorage from the ToolManager */ mitk::DataStorage *GetDataStorage(); mitk::DataNode *GetRefNode(); void SetOutputBuffer(LabelSetImage::Pointer); protected: /** * @brief Construct a new nnUNet Tool object and temp directory. * */ nnUNetTool(); /** * @brief Destroy the nnUNet Tool object and deletes the temp directory. * */ ~nnUNetTool(); /** * @brief Overriden method from the tool manager to execute the segmentation * Implementation: * 1. Saves the inputAtTimeStep in a temporary directory. * 2. Copies other modalities, renames and saves in the temporary directory, if required. * 3. Sets RESULTS_FOLDER and CUDA_VISIBLE_DEVICES variables in the environment. * 3. Iterates through the parameter queue (m_ParamQ) and executes "nnUNet_predict" command with the parameters * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as * LabelSetImage and returns. * * @param inputAtTimeStep * @param timeStep * @return LabelSetImage::Pointer */ - LabelSetImage::Pointer ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType timeStep) override; + void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override; private: std::string m_MitkTempDir; std::string m_nnUNetDirectory; std::string m_ModelDirectory; std::string m_PythonPath; std::string m_PostProcessingJsonDirectory; // bool m_UseGPU; kept for future // bool m_AllInGPU; bool m_MixedPrecision; bool m_Mirror; bool m_NoPip; bool m_MultiModal; bool m_Ensemble = false; bool m_Predict; LabelSetImage::Pointer m_OutputBuffer; unsigned int m_GpuId; const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz"; }; } // namespace mitk #endif diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index e8f1ecffb7..d931118bc0 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,110 +1,107 @@ 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/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/mitkSegWithPreviewTool.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/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.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/mitkPickingTool.cpp Interactions/mitknnUnetTool.cpp Interactions/mitkSegmentationInteractor.cpp #SO Interactions/mitkProcessExecutor.cpp 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 AI_48x48.png AI_Cursor_32x32.png Erase_48x48.png Erase_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 Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml Interactions/LiveWireTool.xml Interactions/PickingTool.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/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp deleted file mode 100644 index 2b6853bb74..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/*============================================================================ - -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 "QmitkAdaptiveRegionGrowingToolGUI.h" - -#include - -#include "mitkITKImageImport.h" -#include "mitkImageAccessByItk.h" -#include "mitkImageTimeSelector.h" -#include "mitkNodePredicateDataType.h" -#include "mitkProperties.h" -#include "mitkTransferFunctionProperty.h" - -#include "mitkImageStatisticsHolder.h" - -#include "itkMaskImageFilter.h" -#include "itkNumericTraits.h" -#include -#include -#include -#include - -#include "QmitkConfirmSegmentationDialog.h" -#include "itkOrImageFilter.h" -#include "mitkImageCast.h" - -#include "mitkImagePixelReadAccessor.h" -#include "mitkPixelTypeMultiplex.h" - -#include "mitkImageCast.h" - -MITK_TOOL_GUI_MACRO(, QmitkAdaptiveRegionGrowingToolGUI, "") - -QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget *parent) - : QmitkToolGUI(), - m_DataStorage(nullptr), - m_UseVolumeRendering(false), - m_UpdateSuggestedThreshold(true), - m_SuggestedThValue(0.0) -{ - this->setParent(parent); - - m_Controls.setupUi(this); - - m_Controls.m_ThresholdSlider->setDecimals(1); - m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); - - m_Controls.m_PreviewSlider->setEnabled(false); - m_Controls.m_PreviewSlider->setSingleStep(0.5); - // Not yet available - // m_Controls.m_PreviewSlider->InvertedAppearance(true); - - //3D preview doesn't work: T24430. Postponed until reimplementation of segmentation - m_Controls.m_cbVolumeRendering->setVisible(false); - - this->CreateConnections(); - this->SetDataNodeNames("labeledRGSegmentation", "RGResult", "RGFeedbackSurface", "maskedSegmentation"); - - connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); -} - -QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() -{ - // Removing the observer of the PointSet node - if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) - { - m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); - m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetMoveObserverTag); - } - this->RemoveHelperNodes(); -} - -void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool *tool) -{ - m_RegionGrow3DTool = dynamic_cast(tool); - if (m_RegionGrow3DTool.IsNotNull()) - { - SetInputImageNode(this->m_RegionGrow3DTool->GetReferenceData()); - this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); - this->EnableControls(true); - - // Watch for point added or modified - itk::SimpleMemberCommand::Pointer pointAddedCommand = - itk::SimpleMemberCommand::New(); - pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); - m_PointSetAddObserverTag = - m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); - m_PointSetMoveObserverTag = - m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); - } - else - { - this->EnableControls(false); - } -} - -void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() -{ - mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - if (imageNode.IsNotNull()) - { - m_DataStorage->Remove(imageNode); - } - - mitk::DataNode::Pointer maskedSegmentationNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); - if (maskedSegmentationNode.IsNotNull()) - { - m_DataStorage->Remove(maskedSegmentationNode); - } -} - -void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() -{ - // Connecting GUI components - connect((QObject *)(m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); - connect(m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); - connect((QObject *)(m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); - connect( - m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); - connect( - m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); -} - -void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, - std::string binaryImage, - std::string surface, - std::string maskedSegmentation) -{ - m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; - m_NAMEFORBINARYIMAGE = binaryImage; - m_NAMEFORSURFACE = surface; - m_NAMEFORMASKEDSEGMENTATION = maskedSegmentation; -} - -void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage *dataStorage) -{ - m_DataStorage = dataStorage; -} - -void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode *node) -{ - m_InputImageNode = node; - mitk::Image *inputImage = dynamic_cast(m_InputImageNode->GetData()); - if (inputImage) - { - mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); - mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); - m_Controls.m_ThresholdSlider->setMaximum(max); - m_Controls.m_ThresholdSlider->setMinimum(min); - // Just for initialization - m_Controls.m_ThresholdSlider->setMaximumValue(max); - m_Controls.m_ThresholdSlider->setMinimumValue(min); - } -} - -template -static void AccessPixel(mitk::PixelType /*ptype*/, mitk::Image* im, mitk::Point3D p, int& val) -{ - mitk::ImagePixelReadAccessor access(im); - val = access.GetPixelByWorldCoordinates(p); -} - -/**Overloaded const verison*/ -template -static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image* im, mitk::Point3D p, int& val) -{ - mitk::ImagePixelReadAccessor access(im); - val = access.GetPixelByWorldCoordinates(p); -} - -void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() -{ - if (m_RegionGrow3DTool.IsNull()) - return; - - mitk::DataNode *node = m_RegionGrow3DTool->GetPointSetNode(); - - if (node != nullptr) - { - mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); - - if (pointSet.IsNull()) - { - QMessageBox::critical(nullptr, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); - return; - } - - m_Controls.m_lblSetSeedpoint->setText(""); - - const mitk::Image *image = dynamic_cast(m_InputImageNode->GetData()); - const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - - auto image3D = GetImageByTimePoint(image, timePoint); - - if (nullptr == image3D) - { - MITK_WARN << "Cannot run segementation. Currently selected timepoint is not in the time bounds of the selected " - "reference image. Time point: " - << timePoint; - return; - } - - if (!pointSet->GetTimeGeometry()->IsValidTimePoint(timePoint)) - return; - - mitk::Point3D seedPoint = - pointSet - ->GetPointSet(static_cast(pointSet->GetTimeGeometry()->TimePointToTimeStep(timePoint))) - ->GetPoints() - ->ElementAt(0); - - if (image3D->GetGeometry()->IsInside(seedPoint)) - mitkPixelTypeMultiplex3( - AccessPixel, image3D->GetChannelDescriptor().GetPixelType(), image3D, seedPoint, m_SeedpointValue) else return; - - /* In this case the seedpoint is placed e.g. in the lung or bronchialtree - * The lowerFactor sets the windowsize depending on the regiongrowing direction - */ - m_CurrentRGDirectionIsUpwards = true; - if (m_SeedpointValue < -500) - { - m_CurrentRGDirectionIsUpwards = false; - } - - // Initializing the region by the area around the seedpoint - m_SeedPointValueMean = 0; - - itk::Index<3> currentIndex, runningIndex; - mitk::ScalarType pixelValues[125]; - unsigned int pos(0); - - image3D->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); - runningIndex = currentIndex; - - for (int i = runningIndex[0] - 2; i <= runningIndex[0] + 2; i++) - { - for (int j = runningIndex[1] - 2; j <= runningIndex[1] + 2; j++) - { - for (int k = runningIndex[2] - 2; k <= runningIndex[2] + 2; k++) - { - currentIndex[0] = i; - currentIndex[1] = j; - currentIndex[2] = k; - - if (image3D->GetGeometry()->IsIndexInside(currentIndex)) - { - int component = 0; - m_InputImageNode->GetIntProperty("Image.Displayed Component", component); - mitkPixelTypeMultiplex4(mitk::FastSinglePixelAccess, - image3D->GetChannelDescriptor().GetPixelType(), - image3D, - nullptr, - currentIndex, - pixelValues[pos]); - - pos++; - } - else - { - pixelValues[pos] = std::numeric_limits::min(); - pos++; - } - } - } - } - - // Now calculation mean of the pixelValues - // Now calculation mean of the pixelValues - unsigned int numberOfValues(0); - for (auto &pixelValue : pixelValues) - { - if (pixelValue > std::numeric_limits::min()) - { - m_SeedPointValueMean += pixelValue; - numberOfValues++; - } - } - m_SeedPointValueMean = m_SeedPointValueMean / numberOfValues; - - mitk::ScalarType var = 0; - if (numberOfValues > 1) - { - for (auto &pixelValue : pixelValues) - { - if (pixelValue > std::numeric_limits::min()) - { - var += (pixelValue - m_SeedPointValueMean) * (pixelValue - m_SeedPointValueMean); - } - } - var /= numberOfValues - 1; - } - mitk::ScalarType stdDev = sqrt(var); - - /* - * Here the upper- and lower threshold is calculated: - * The windowSize is 20% of the maximum range of the intensity values existing in the current image - * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is - * meanSeedValue+0.85*windowsSize - * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is - * meanSeedValue+0.15*windowsSize - */ - const auto timeStepOfImage = image->GetTimeGeometry()->TimePointToTimeStep(timePoint); - mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(timeStepOfImage); - mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(timeStepOfImage); - - mitk::ScalarType windowSize = max - min; - - windowSize = 0.15 * windowSize; - - if (m_CurrentRGDirectionIsUpwards) - { - m_LOWERTHRESHOLD = m_SeedPointValueMean - stdDev; - m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; - if (m_UPPERTHRESHOLD > max) - m_UPPERTHRESHOLD = max; - m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); - m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); - } - else - { - m_UPPERTHRESHOLD = m_SeedPointValueMean; - if (m_SeedpointValue > m_SeedPointValueMean) - m_UPPERTHRESHOLD = m_SeedpointValue; - m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; - if (m_LOWERTHRESHOLD < min) - m_LOWERTHRESHOLD = min; - m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); - m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); - } - } -} - -mitk::Image::ConstPointer QmitkAdaptiveRegionGrowingToolGUI::GetImageByTimePoint(const mitk::Image *image, - mitk::TimePointType timePoint) const -{ - if (nullptr == image) - return image; - - if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) - return nullptr; - - if (image->GetDimension() != 4) - return image; - - auto imageTimeSelector = mitk::ImageTimeSelector::New(); - - imageTimeSelector->SetInput(image); - imageTimeSelector->SetTimeNr(static_cast(image->GetTimeGeometry()->TimePointToTimeStep(timePoint))); - - imageTimeSelector->UpdateLargestPossibleRegion(); - - return imageTimeSelector->GetOutput(); -} - -void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() -{ - if (m_InputImageNode.IsNull()) - { - QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); - return; - } - - mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); - - if (node.IsNull()) - { - QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please insert a seed point inside the " - "image.\n\nFirst press the \"Define Seed " - "Point\" button,\nthen click left mouse " - "button inside the image."); - return; - } - - // safety if no pointSet or pointSet empty - mitk::PointSet::Pointer seedPointSet = dynamic_cast(node->GetData()); - if (seedPointSet.IsNull()) - { - m_Controls.m_pbRunSegmentation->setEnabled(true); - QMessageBox::information( - nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); - return; - } - - mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); - - if (orgImage.IsNotNull()) - { - const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - - if (!seedPointSet->GetTimeGeometry()->IsValidTimePoint(timePoint)) - mitkThrow() << "Point set is not defined for specified time point. Time point: " << timePoint; - - int timeStep = static_cast(seedPointSet->GetTimeGeometry()->TimePointToTimeStep(timePoint)); - - if (!(seedPointSet->GetSize(timeStep))) - { - m_Controls.m_pbRunSegmentation->setEnabled(true); - QMessageBox::information( - nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); - return; - } - - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - - mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); - - auto image3D = GetImageByTimePoint(orgImage, timePoint); - - if (image3D.IsNotNull()) - { - // QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting - AccessByItk_2(image3D, StartRegionGrowing, image3D->GetGeometry(), seedPoint); - // QApplication::restoreOverrideCursor();//reset cursor - } - else - { - QApplication::restoreOverrideCursor(); // reset cursor - QMessageBox::information( - nullptr, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); - return; - } - } - EnableControls(true); // Segmentation ran successfully, so enable all controls. - node->SetVisibility(true); - QApplication::restoreOverrideCursor(); // reset cursor -} - -template -void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(const itk::Image *itkImage, - const mitk::BaseGeometry *imageGeometry, - const mitk::PointSet::PointType seedPoint) -{ - typedef itk::Image InputImageType; - typedef typename InputImageType::IndexType IndexType; - typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; - typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - typedef itk::MaskImageFilter MaskImageFilterType; - - if (!imageGeometry->IsInside(seedPoint)) - { - QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor - QMessageBox::information(nullptr, - "Segmentation functionality", - "The seed point is outside of the image! Please choose a position inside the image!"); - return; - } - - IndexType seedIndex; - imageGeometry->WorldToIndex(seedPoint, seedIndex); // convert world coordinates to image indices - - if (m_SeedpointValue > m_UPPERTHRESHOLD || m_SeedpointValue < m_LOWERTHRESHOLD) - { - QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor - QMessageBox::information( - nullptr, - "Segmentation functionality", - "The seed point is outside the defined thresholds! Please set a new seed point or adjust the thresholds."); - MITK_INFO << "Mean: " << m_SeedPointValueMean; - return; - } - - // Setting the direction of the regiongrowing. For dark structures e.g. the lung the regiongrowing - // is performed starting at the upper value going to the lower one - regionGrower->SetGrowingDirectionIsUpwards(m_CurrentRGDirectionIsUpwards); - regionGrower->SetInput(itkImage); - regionGrower->AddSeed(seedIndex); - // In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. - // Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter - mitk::ScalarType maxPixelValue = m_Controls.m_ThresholdSlider->maximum(); - mitk::ScalarType minPixelValue = m_Controls.m_ThresholdSlider->minimum(); - - if ((m_LOWERTHRESHOLD - minPixelValue) >= 1) - { - regionGrower->SetLower(m_LOWERTHRESHOLD - 1); - } - else - { - regionGrower->SetLower(m_LOWERTHRESHOLD); - } - - if ((maxPixelValue - m_UPPERTHRESHOLD) >= 1) - { - regionGrower->SetUpper(m_UPPERTHRESHOLD + 1); - } - else - { - regionGrower->SetUpper(m_UPPERTHRESHOLD); - } - - - try - { - regionGrower->Update(); - } - catch (itk::ExceptionObject &exc) - { - QMessageBox errorInfo; - errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); - errorInfo.setIcon(QMessageBox::Critical); - errorInfo.setText("An error occurred during region growing!"); - errorInfo.setDetailedText(exc.what()); - errorInfo.exec(); - return; // can't work - } - catch (...) - { - QMessageBox::critical(nullptr, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); - return; - } - - mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); - // initialize slider - m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); - - mitk::ScalarType max = m_SeedpointValue + resultImage->GetStatistics()->GetScalarValueMax(); - if (max < m_UPPERTHRESHOLD) - m_Controls.m_PreviewSlider->setMaximum(max); - else - m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); - - this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); - - if (m_CurrentRGDirectionIsUpwards) - { - m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean - 1); - } - else - { - m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean + 1); - } - this->m_SliderInitialized = true; - - // create new node and then delete the old one if there is one - mitk::DataNode::Pointer newNode = mitk::DataNode::New(); - newNode->SetData(resultImage); - - // set some properties - newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); - newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - newNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); - newNode->SetProperty("layer", mitk::IntProperty::New(1)); - newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); - - // delete the old image, if there was one: - mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - m_DataStorage->Remove(binaryNode); - - // now add result to data tree - m_DataStorage->Add(newNode, m_InputImageNode); - - typename InputImageType::Pointer inputImageItk; - mitk::CastToItkImage(resultImage, inputImageItk); - // volume rendering preview masking - typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); - thresholdFilter->SetInput(inputImageItk); - thresholdFilter->SetInsideValue(1); - thresholdFilter->SetOutsideValue(0); - - double sliderVal = this->m_Controls.m_PreviewSlider->value(); - if (m_CurrentRGDirectionIsUpwards) - { - thresholdFilter->SetLowerThreshold(sliderVal); - thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); - } - else - { - thresholdFilter->SetLowerThreshold(itk::NumericTraits::NonpositiveMin()); - thresholdFilter->SetUpperThreshold(sliderVal); - } - thresholdFilter->SetInPlace(false); - - typename MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); - maskFilter->SetInput(inputImageItk); - maskFilter->SetInPlace(false); - maskFilter->SetMaskImage(thresholdFilter->GetOutput()); - maskFilter->SetOutsideValue(0); - maskFilter->UpdateLargestPossibleRegion(); - - mitk::Image::Pointer mitkMask; - mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMask); - mitk::DataNode::Pointer maskedNode = mitk::DataNode::New(); - maskedNode->SetData(mitkMask); - - // set some properties - maskedNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORMASKEDSEGMENTATION)); - maskedNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - maskedNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); - maskedNode->SetProperty("layer", mitk::IntProperty::New(1)); - maskedNode->SetProperty("opacity", mitk::FloatProperty::New(0.0)); - - // delete the old image, if there was one: - mitk::DataNode::Pointer deprecatedMask = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); - m_DataStorage->Remove(deprecatedMask); - - // now add result to data tree - m_DataStorage->Add(maskedNode, m_InputImageNode); - - this->InitializeLevelWindow(); - - if (m_UseVolumeRendering) - this->EnableVolumeRendering(true); - - m_UpdateSuggestedThreshold = true; // reset first stored threshold value - // Setting progress to finished - mitk::ProgressBar::GetInstance()->Progress(357); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - -void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() -{ - // get the preview from the datatree - mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - - mitk::LevelWindow tempLevelWindow; - newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); - - mitk::ScalarType *level = new mitk::ScalarType(0.0); - mitk::ScalarType *window = new mitk::ScalarType(1.0); - - int upper; - if (m_CurrentRGDirectionIsUpwards) - { - upper = m_UPPERTHRESHOLD - m_SeedpointValue; - } - else - { - upper = m_SeedpointValue - m_LOWERTHRESHOLD; - } - - tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); - - // get the suggested threshold from the detected leakage-point and adjust the slider - - if (m_CurrentRGDirectionIsUpwards) - { - this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); - *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; - } - else - { - this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); - *level = (m_SeedpointValue)-m_LOWERTHRESHOLD + 0.5; - } - - tempLevelWindow.SetLevelWindow(*level, *window); - newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); - // update the widgets - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - - m_SliderInitialized = true; - - // inquiry need to fix bug#1828 - static int lastSliderPosition = 0; - if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) - { - this->ChangeLevelWindow(lastSliderPosition); - } - lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1; - - if (m_UseVolumeRendering) - this->UpdateVolumeRenderingThreshold((int)(*level + 0.5)); // lower threshold for labeled image -} - -void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) -{ - if (m_SliderInitialized) - { - // do nothing, if no preview exists - mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - if (newNode.IsNull()) - return; - - mitk::LevelWindow tempLevelWindow; - - newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // get the levelWindow associated with the preview - - mitk::ScalarType level; // = this->m_UPPERTHRESHOLD - newValue + 0.5; - mitk::ScalarType *window = new mitk::ScalarType(1); - - // adjust the levelwindow according to the position of the slider (newvalue) - if (m_CurrentRGDirectionIsUpwards) - { - level = m_UPPERTHRESHOLD - newValue + 0.5; - tempLevelWindow.SetLevelWindow(level, *window); - } - else - { - level = newValue - m_LOWERTHRESHOLD + 0.5; - tempLevelWindow.SetLevelWindow(level, *window); - } - - newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); - - if (m_UseVolumeRendering) - this->UpdateVolumeRenderingThreshold((int)(level - 0.5)); // lower threshold for labeled image - - newNode->SetVisibility(true); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } -} - -void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() -{ - // moves the slider one step to the left, when the "-"-button is pressed - if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) - { - int newValue = this->m_Controls.m_PreviewSlider->value() - 1; - this->ChangeLevelWindow(newValue); - this->m_Controls.m_PreviewSlider->setValue(newValue); - } -} - -void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() -{ - // moves the slider one step to the right, when the "+"-button is pressed - if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) - { - int newValue = this->m_Controls.m_PreviewSlider->value() + 1; - this->ChangeLevelWindow(newValue); - this->m_Controls.m_PreviewSlider->setValue(newValue); - } -} - -void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() -{ - // get image node - if (m_InputImageNode.IsNull()) - { - QMessageBox::critical(nullptr, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); - return; - } - // get image data - mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); - if (orgImage.IsNull()) - { - QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Image found!"); - return; - } - // get labeled segmentation - mitk::Image::Pointer labeledSeg = - (mitk::Image *)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - if (labeledSeg.IsNull()) - { - QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Segmentation Preview found!"); - return; - } - - mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - if (newNode.IsNull()) - return; - - QmitkConfirmSegmentationDialog dialog; - QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); - - dialog.SetSegmentationName(segName); - int result = dialog.exec(); - - switch (result) - { - case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: - m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); - break; - case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: - m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); - break; - case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: - return; - } - - mitk::Image::Pointer img = dynamic_cast(newNode->GetData()); - AccessByItk(img, ITKThresholding); - - // disable volume rendering preview after the segmentation node was created - this->EnableVolumeRendering(false); - newNode->SetVisibility(false); - m_Controls.m_cbVolumeRendering->setChecked(false); - // TODO disable slider etc... - - if (m_RegionGrow3DTool.IsNotNull()) - { - m_RegionGrow3DTool->ConfirmSegmentation(); - } -} - -template -void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image *itkImage) -{ - mitk::Image::Pointer originalSegmentation = - dynamic_cast(this->m_RegionGrow3DTool->GetTargetSegmentationNode()->GetData()); - - const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - - if (!originalSegmentation->GetTimeGeometry()->IsValidTimePoint(timePoint)) - mitkThrow() << "Segmentation is not defined for specified time point. Time point: " << timePoint; - - int timeStep = static_cast(originalSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint)); - - - if (originalSegmentation) - { - typedef itk::Image InputImageType; - typedef itk::Image SegmentationType; - - // select single 3D volume if we have more than one time step - typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); - if (originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) - { - mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); - timeSelector->SetInput(originalSegmentation); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - CastToItkImage(timeSelector->GetOutput(), originalSegmentationInITK); - } - else // use original - { - CastToItkImage(originalSegmentation, originalSegmentationInITK); - } - - // Fill current preiview image in segmentation image - originalSegmentationInITK->FillBuffer(0); - itk::ImageRegionIterator itOutput(originalSegmentationInITK, - originalSegmentationInITK->GetLargestPossibleRegion()); - itk::ImageRegionIterator itInput(itkImage, itkImage->GetLargestPossibleRegion()); - itOutput.GoToBegin(); - itInput.GoToBegin(); - - // calculate threhold from slider value - int currentTreshold = 0; - if (m_CurrentRGDirectionIsUpwards) - { - currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; - } - else - { - currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; - } - - // iterate over image and set pixel in segmentation according to thresholded labeled image - while (!itOutput.IsAtEnd() && !itInput.IsAtEnd()) - { - // Use threshold slider to determine if pixel is set to 1 - if (itInput.Value() != 0 && itInput.Value() >= static_cast::PixelType>(currentTreshold)) - { - itOutput.Set(1); - } - - ++itOutput; - ++itInput; - } - - // combine current working segmentation image with our region growing result - originalSegmentation->SetVolume((void *)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), - timeStep); - - originalSegmentation->Modified(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } -} - -void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) -{ - if (m_RegionGrow3DTool.IsNull()) - return; - - // Check if seed point is already set, if not leave RunSegmentation disabled - // if even m_DataStorage is nullptr leave node nullptr - mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); - if (node.IsNull()) - { - this->m_Controls.m_pbRunSegmentation->setEnabled(false); - } - else - { - this->m_Controls.m_pbRunSegmentation->setEnabled(enable); - } - - // Check if a segmentation exists, if not leave segmentation dependent disabled. - // if even m_DataStorage is nullptr leave node nullptr - node = m_DataStorage ? m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE) : nullptr; - if (node.IsNull()) - { - this->m_Controls.m_PreviewSlider->setEnabled(false); - this->m_Controls.m_pbConfirmSegementation->setEnabled(false); - } - else - { - this->m_Controls.m_PreviewSlider->setEnabled(enable); - this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); - } - - this->m_Controls.m_cbVolumeRendering->setEnabled(enable); -} - -void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) -{ - mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); - - if (node.IsNull()) - return; - - node->SetBoolProperty("volumerendering", enable); - - double val = this->m_Controls.m_PreviewSlider->value(); - this->ChangeLevelWindow(val); - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - -void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int) -{ - typedef short PixelType; - typedef itk::Image InputImageType; - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - typedef itk::MaskImageFilter MaskImageFilterType; - - mitk::DataNode::Pointer grownImageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - mitk::Image::Pointer grownImage = dynamic_cast(grownImageNode->GetData()); - - if (!grownImage) - { - MITK_ERROR << "Missing data node for labeled segmentation image."; - return; - } - - InputImageType::Pointer itkGrownImage; - mitk::CastToItkImage(grownImage, itkGrownImage); - - ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); - thresholdFilter->SetInput(itkGrownImage); - thresholdFilter->SetInPlace(false); - - double sliderVal = this->m_Controls.m_PreviewSlider->value(); - PixelType threshold = itk::NumericTraits::min(); - if (m_CurrentRGDirectionIsUpwards) - { - threshold = static_cast(m_UPPERTHRESHOLD - sliderVal + 0.5); - - thresholdFilter->SetLowerThreshold(threshold); - thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); - } - else - { - threshold = sliderVal - m_LOWERTHRESHOLD + 0.5; - - thresholdFilter->SetLowerThreshold(itk::NumericTraits::min()); - thresholdFilter->SetUpperThreshold(threshold); - } - thresholdFilter->UpdateLargestPossibleRegion(); - - MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); - maskFilter->SetInput(itkGrownImage); - maskFilter->SetInPlace(false); - maskFilter->SetMaskImage(thresholdFilter->GetOutput()); - maskFilter->SetOutsideValue(0); - maskFilter->UpdateLargestPossibleRegion(); - - mitk::Image::Pointer mitkMaskedImage; - mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMaskedImage); - mitk::DataNode::Pointer maskNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); - maskNode->SetData(mitkMaskedImage); -} - -void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) -{ - m_UseVolumeRendering = on; - - this->EnableVolumeRendering(on); -} - -void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue(double lowerThreshold) -{ - m_LOWERTHRESHOLD = lowerThreshold; -} - -void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue(double upperThreshold) -{ - m_UPPERTHRESHOLD = upperThreshold; -} - -void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() -{ - // make the segmentation preview node invisible - mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); - if (node.IsNotNull()) - { - node->SetVisibility(false); - } - - // disable volume rendering preview after the segmentation node was created - this->EnableVolumeRendering(false); - m_Controls.m_cbVolumeRendering->setChecked(false); -} - -void QmitkAdaptiveRegionGrowingToolGUI::Activated() -{ -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h deleted file mode 100644 index 79f848ea27..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h +++ /dev/null @@ -1,229 +0,0 @@ -/*============================================================================ - -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 QMITK_QmitkAdaptiveRegionGrowingToolGUI_H -#define QMITK_QmitkAdaptiveRegionGrowingToolGUI_H - -#include "itkImage.h" -#include "mitkDataStorage.h" -#include "mitkGeometry3D.h" -#include "mitkPointSet.h" - -#include "qwidget.h" -#include "ui_QmitkAdaptiveRegionGrowingToolGUIControls.h" - -#include - -#include "QmitkToolGUI.h" - -#include "mitkAdaptiveRegionGrowingTool.h" - -class DataNode; -class QmitkAdaptiveRegionGrowingToolGUIControls; - -/*! -* -* \brief QmitkAdaptiveRegionGrowingToolGUI -* -* Adaptive Region Growing View class of the segmentation. -* -*/ - -class MITKSEGMENTATIONUI_EXPORT QmitkAdaptiveRegionGrowingToolGUI : public QmitkToolGUI -{ - Q_OBJECT - -public: - /** - * @brief mitkClassMacro - */ - mitkClassMacro(QmitkAdaptiveRegionGrowingToolGUI, QmitkToolGUI); - - itkFactorylessNewMacro(Self); - - itkCloneMacro(Self); - - QmitkAdaptiveRegionGrowingToolGUI(QWidget *parent = nullptr); - - /** \brief Method to create the connections for the component. This Method is obligatory even if no connections is - * needed*/ - virtual void CreateConnections(); - - ///** \brief Method to set the default data storage.*/ - virtual void SetDataStorage(mitk::DataStorage *dataStorage); - - /** - * @brief Method to set the name of a data node. - * @param labledSegmentation Name of the labeled segmentation - * @param binaryImage Name of the binary image - * @param surface Name of the surface - * @param maskedSegmentation - */ - void SetDataNodeNames(std::string labledSegmentation, - std::string binaryImage, - /*std::string vesselTree,*/ std::string surface, - std::string maskedSegmentation); - - /** - * @brief Method to enable/disable controls for region growing - * - * This method checks if a seed point is set and a segmentation exists. - * @param enable enable/disable controls - */ - void EnableControls(bool enable); - - /** - * @brief Method to set the input image node - * @param node data node - */ - void SetInputImageNode(mitk::DataNode *node); - - void Deactivated(); - void Activated(); - - /** - * @brief The created GUI from the .ui-File. This Attribute is obligatory - */ - Ui::QmitkAdaptiveRegionGrowingToolGUIControls m_Controls; - -protected slots: - - /** - * @brief Method to start the segmentation - * - * This method is called, when the "Start Segmentation" button is clicked. - */ - void RunSegmentation(); - - /** - * @brief Method to change the level window - * - * This method is called, when the level window slider is changed via the slider in the control widget - * @param newValue new value - */ - void ChangeLevelWindow(double newValue); - - /** - * @brief Method to increase the preview slider - * - * This method is called, when the + button is clicked and increases the value by 1 - */ - void IncreaseSlider(); - - /** - * @brief Method to decrease the preview slider - * - * This method is called, when the - button is clicked and decreases the value by 1 - */ - void DecreaseSlider(); - - /** - * @brief Method to confirm the preview segmentation - * - * This method is called, when the "Confirm Segmentation" button is clicked. - */ - void ConfirmSegmentation(); - - /** - * @brief Method to switch the volume rendering on/off - * @param on - */ - void UseVolumeRendering(bool on); - - /** - * @brief Method to set the lower threshold - * - * This method is called, when the minimum threshold slider has changed - * @param lowerThreshold lower threshold - */ - void SetLowerThresholdValue(double lowerThreshold); - - /** - * @brief Method to set upper threshold - * - * This Method is called, when the maximum threshold slider has changed - * @param upperThreshold upper threshold - */ - void SetUpperThresholdValue(double upperThreshold); - - /** - * @brief Method to determine which tool to activate - * - * This method listens to the tool manager and activates this tool if requested otherwise disables this view - */ - void OnNewToolAssociated(mitk::Tool *); - -protected: - mitk::AdaptiveRegionGrowingTool::Pointer m_RegionGrow3DTool; - - /** \brief Destructor. */ - ~QmitkAdaptiveRegionGrowingToolGUI() override; - - mitk::DataStorage *m_DataStorage; - - mitk::DataNode::Pointer m_InputImageNode; - - /** - * @brief Method to calculate parameter settings, when a seed point is set - */ - void OnPointAdded(); - - /** - * @brief Method to extract a 3D image based on a given time point that can be taken from the SliceNavigationController - * - * This ensures that the seed point is taken from the current selected 3D image - */ - mitk::Image::ConstPointer GetImageByTimePoint(const mitk::Image *image, - mitk::TimePointType timePoint) const; - -private: - std::string m_NAMEFORORGIMAGE; - std::string m_NAMEFORLABLEDSEGMENTATIONIMAGE; - std::string m_NAMEFORBINARYIMAGE; - std::string m_NAMEFORSURFACE; - std::string m_NAMEFORMASKEDSEGMENTATION; - - mitk::ScalarType m_LOWERTHRESHOLD; // Hounsfield value - mitk::ScalarType m_UPPERTHRESHOLD; // Hounsfield value - mitk::ScalarType m_SeedPointValueMean; - - void RemoveHelperNodes(); - - int m_DetectedLeakagePoint; - - bool m_CurrentRGDirectionIsUpwards; // defines fixed threshold (true = LOWERTHRESHOLD fixed, false = UPPERTHRESHOLD - // fixed) - - int m_SeedpointValue; - bool m_SliderInitialized; - bool m_UseVolumeRendering; - bool m_UpdateSuggestedThreshold; - float m_SuggestedThValue; - - long m_PointSetAddObserverTag; - long m_PointSetMoveObserverTag; - - template - void StartRegionGrowing(const itk::Image *itkImage, - const mitk::BaseGeometry *imageGeometry, - const mitk::PointSet::PointType seedPoint); - - template - void ITKThresholding(itk::Image *inputImage); - - void InitializeLevelWindow(); - - void EnableVolumeRendering(bool enable); - - void UpdateVolumeRenderingThreshold(int thValue); -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui deleted file mode 100644 index 2b91deefe8..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui +++ /dev/null @@ -1,227 +0,0 @@ - - - QmitkAdaptiveRegionGrowingToolGUIControls - - - - 0 - 0 - 264 - 171 - - - - - 0 - 0 - - - - - 0 - 0 - - - - QmitkAdaptiveRegionGrowingWidget - - - Move to adjust the segmentation - - - - 0 - - - - - - 0 - 0 - - - - Shift+Click to place seedpoint - - - 0 - - - - - - - Define thresholds: - - - true - - - - 0 - - - 6 - - - 0 - - - 0 - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - - 0 - 0 - - - - - - - Press to start the segmentation of the tubular structure. - - - Run Segmentation - - - Alt+R - - - - - - - false - - - - 0 - 0 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Enable/disable GPU volume rendering for the segmentation preview.</span></p></body></html> - - - Qt::LeftToRight - - - 3D preview - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Adapt Region Growing: - - - true - - - - 0 - - - 6 - - - 0 - - - 0 - - - - - - - - - - - false - - - - - - Press to confirm the segmentation and calculate 3D representation. - - - &Confirm Segmentation - - - Alt+C - - - - - - - - - ctkRangeWidget - QWidget -
ctkRangeWidget.h
- 1 -
- - ctkSliderWidget - QWidget -
ctkSliderWidget.h
- 1 -
-
- - -
diff --git a/Modules/SegmentationUI/Qmitk/QmitkAutoMLSegmentationToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkAutoMLSegmentationToolGUIBase.cpp deleted file mode 100644 index 863e753c08..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkAutoMLSegmentationToolGUIBase.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/*============================================================================ - -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 "QmitkAutoMLSegmentationToolGUIBase.h" -#include "mitkAutoMLSegmentationWithPreviewTool.h" - -#include - -QmitkAutoMLSegmentationToolGUIBase::QmitkAutoMLSegmentationToolGUIBase() : QmitkAutoSegmentationToolGUIBase(false) -{ - auto enableMLSelectedDelegate = [this](bool enabled) - { - bool result = enabled; - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - result = !tool->GetSelectedLabels().empty() && enabled; - } - else - { - result = false; - } - - return result; - }; - - m_EnableConfirmSegBtnFnc = enableMLSelectedDelegate; -} - -void QmitkAutoMLSegmentationToolGUIBase::InitializeUI(QBoxLayout* mainLayout) -{ - m_LabelSelectionList = new QmitkSimpleLabelSetListWidget(this); - m_LabelSelectionList->setObjectName(QString::fromUtf8("m_LabelSelectionList")); - QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - sizePolicy2.setHorizontalStretch(0); - sizePolicy2.setVerticalStretch(0); - sizePolicy2.setHeightForWidth(m_LabelSelectionList->sizePolicy().hasHeightForWidth()); - m_LabelSelectionList->setSizePolicy(sizePolicy2); - m_LabelSelectionList->setMaximumSize(QSize(10000000, 10000000)); - - mainLayout->addWidget(m_LabelSelectionList); - - connect(m_LabelSelectionList, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkAutoMLSegmentationToolGUIBase::OnLabelSelectionChanged); - - Superclass::InitializeUI(mainLayout); -} - -void QmitkAutoMLSegmentationToolGUIBase::OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - mitk::AutoMLSegmentationWithPreviewTool::SelectedLabelVectorType labelIDs; - for (const auto& label : selectedLabels) - { - labelIDs.push_back(label->GetValue()); - } - - tool->SetSelectedLabels(labelIDs); - tool->UpdatePreview(); - this->EnableWidgets(true); //used to actualize the ConfirmSeg btn via the delegate; - } -} - -void QmitkAutoMLSegmentationToolGUIBase::EnableWidgets(bool enabled) -{ - Superclass::EnableWidgets(enabled); - if (nullptr != m_LabelSelectionList) - { - m_LabelSelectionList->setEnabled(enabled); - } -} - -void QmitkAutoMLSegmentationToolGUIBase::SetLabelSetPreview(const mitk::LabelSetImage* preview) -{ - if (nullptr != m_LabelSelectionList) - { - m_LabelSelectionList->SetLabelSetImage(preview); - } -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp index 930eacadb7..f6231dea49 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp @@ -1,187 +1,187 @@ /*============================================================================ 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 "QmitkBinaryThresholdToolGUIBase.h" #include "mitkBinaryThresholdBaseTool.h" #include "mitkBinaryThresholdTool.h" #include #include #include #include -QmitkBinaryThresholdToolGUIBase::QmitkBinaryThresholdToolGUIBase(bool ulMode) : QmitkAutoSegmentationToolGUIBase(false), m_ULMode(ulMode) +QmitkBinaryThresholdToolGUIBase::QmitkBinaryThresholdToolGUIBase(bool ulMode) : QmitkSegWithPreviewToolGUIBase(false), m_ULMode(ulMode) { } QmitkBinaryThresholdToolGUIBase::~QmitkBinaryThresholdToolGUIBase() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged); tool->ThresholdingValuesChanged -= mitk::MessageDelegate2( this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged); } } void QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat) { m_InternalUpdate = true; if (m_ULMode) { if (!isFloat) { m_ThresholdRange->setRange(int(lower), int(upper)); m_ThresholdRange->setSingleStep(1); m_ThresholdRange->setDecimals(0); } else { m_ThresholdRange->setRange(lower, upper); } } else { if (!isFloat) { m_ThresholdSlider->setRange(int(lower), int(upper)); m_ThresholdSlider->setSingleStep(1); m_ThresholdSlider->setDecimals(0); } else { m_ThresholdSlider->setRange(lower, upper); } } m_InternalUpdate = false; } void QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper) { if (m_ULMode) { m_ThresholdRange->setValues(lower, upper); } else { m_ThresholdSlider->setValue(lower); } } void QmitkBinaryThresholdToolGUIBase::OnThresholdRangeChanged(double min, double max) { auto tool = this->GetConnectedToolAs(); if (nullptr != tool && !m_InternalUpdate) { tool->SetThresholdValues(min, max); } } void QmitkBinaryThresholdToolGUIBase::OnThresholdSliderChanged(double value) { auto tool = this->GetConnectedToolAs(); if (nullptr != tool && !m_InternalUpdate) { tool->SetThresholdValue(value); } } -void QmitkBinaryThresholdToolGUIBase::DisconnectOldTool(mitk::AutoSegmentationWithPreviewTool* oldTool) +void QmitkBinaryThresholdToolGUIBase::DisconnectOldTool(mitk::SegWithPreviewTool* oldTool) { Superclass::DisconnectOldTool(oldTool); auto tool = dynamic_cast(oldTool); if (nullptr != tool) { tool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged); tool->ThresholdingValuesChanged -= mitk::MessageDelegate2( this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged); } } -void QmitkBinaryThresholdToolGUIBase::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) +void QmitkBinaryThresholdToolGUIBase::ConnectNewTool(mitk::SegWithPreviewTool* newTool) { Superclass::ConnectNewTool(newTool); auto tool = dynamic_cast(newTool); if (nullptr != tool) { tool->IntervalBordersChanged += mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged); tool->ThresholdingValuesChanged += mitk::MessageDelegate2( this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged); } } void QmitkBinaryThresholdToolGUIBase::InitializeUI(QBoxLayout* mainLayout) { QLabel* label = new QLabel("Threshold :", this); QFont f = label->font(); f.setBold(false); label->setFont(f); mainLayout->addWidget(label); QBoxLayout* layout = new QHBoxLayout(); if (m_ULMode) { m_ThresholdRange = new ctkRangeWidget(); connect( m_ThresholdRange, SIGNAL(valuesChanged(double, double)), this, SLOT(OnThresholdRangeChanged(double, double))); layout->addWidget(m_ThresholdRange); m_ThresholdRange->setSingleStep(0.01); } else { m_ThresholdSlider = new ctkSliderWidget(); connect( m_ThresholdSlider, SIGNAL(valueChanged(double)), this, SLOT(OnThresholdSliderChanged(double))); layout->addWidget(m_ThresholdSlider); m_ThresholdSlider->setSingleStep(0.01); } mainLayout->addLayout(layout); Superclass::InitializeUI(mainLayout); } void QmitkBinaryThresholdToolGUIBase::BusyStateChanged(bool value) { Superclass::BusyStateChanged(value); if (m_ThresholdRange) { m_ThresholdRange->setEnabled(!value); } if (m_ThresholdSlider) { m_ThresholdSlider->setEnabled(!value); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h index 47f25dd544..c1d57564e1 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h +++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h @@ -1,63 +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 QmitkBinaryThresholdToolGUIBase_h_Included #define QmitkBinaryThresholdToolGUIBase_h_Included -#include "QmitkAutoSegmentationToolGUIBase.h" +#include "QmitkSegWithPreviewToolGUIBase.h" #include "ctkRangeWidget.h" #include "ctkSliderWidget.h" #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief Base GUI for mitk::BinaryThresholdTool. This GUI shows a slider to change the tool's threshold and an OK button to accept a preview for actual thresholding. */ -class MITKSEGMENTATIONUI_EXPORT QmitkBinaryThresholdToolGUIBase : public QmitkAutoSegmentationToolGUIBase +class MITKSEGMENTATIONUI_EXPORT QmitkBinaryThresholdToolGUIBase : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: - mitkClassMacro(QmitkBinaryThresholdToolGUIBase, QmitkAutoSegmentationToolGUIBase); + mitkClassMacro(QmitkBinaryThresholdToolGUIBase, QmitkSegWithPreviewToolGUIBase); void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat); void OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper); protected slots: void OnThresholdRangeChanged(double min, double max); void OnThresholdSliderChanged(double value); protected: QmitkBinaryThresholdToolGUIBase(bool ulMode); ~QmitkBinaryThresholdToolGUIBase() override; - void DisconnectOldTool(mitk::AutoSegmentationWithPreviewTool* oldTool) override; - void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) override; + void DisconnectOldTool(mitk::SegWithPreviewTool* oldTool) override; + void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override; void InitializeUI(QBoxLayout* mainLayout) override; void BusyStateChanged(bool) override; ctkRangeWidget* m_ThresholdRange = nullptr; ctkSliderWidget* m_ThresholdSlider = nullptr; /** Indicates if the tool UI is used for a tool with upper an lower threshold (true) ore only with one threshold (false)*/ bool m_ULMode; bool m_InternalUpdate = false; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp new file mode 100644 index 0000000000..2ba34c9581 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp @@ -0,0 +1,149 @@ +/*============================================================================ + +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 "QmitkMultiLabelSegWithPreviewToolGUIBase.h" +#include "mitkSegWithPreviewTool.h" + +#include +#include + +QmitkMultiLabelSegWithPreviewToolGUIBase::QmitkMultiLabelSegWithPreviewToolGUIBase() : QmitkSegWithPreviewToolGUIBase(false) +{ + auto enableMLSelectedDelegate = [this](bool enabled) + { + auto tool = this->GetConnectedToolAs(); + return nullptr != tool + ? (tool->GetLabelTransferMode() == mitk::SegWithPreviewTool::LabelTransferMode::AllLabels || !tool->GetSelectedLabels().empty()) && enabled + : false; + }; + + m_EnableConfirmSegBtnFnc = enableMLSelectedDelegate; +} + +void QmitkMultiLabelSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* mainLayout) +{ + auto radioTransferAll = new QRadioButton("Transfer all labels", this); + radioTransferAll->setToolTip("Transfer all preview label when confirmed."); + radioTransferAll->setChecked(true); + connect(radioTransferAll, &QAbstractButton::toggled, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked); + mainLayout->addWidget(radioTransferAll); + m_RadioTransferAll = radioTransferAll; + + auto radioTransferSelected = new QRadioButton("Transfer selected labels", this); + radioTransferSelected->setToolTip("Transfer the selected preview label when confirmed."); + radioTransferSelected->setChecked(false); + mainLayout->addWidget(radioTransferSelected); + m_RadioTransferSelected = radioTransferSelected; + + m_LabelSelectionList = new QmitkSimpleLabelSetListWidget(this); + m_LabelSelectionList->setObjectName(QString::fromUtf8("m_LabelSelectionList")); + QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(m_LabelSelectionList->sizePolicy().hasHeightForWidth()); + m_LabelSelectionList->setSizePolicy(sizePolicy2); + m_LabelSelectionList->setMaximumSize(QSize(10000000, 10000000)); + m_LabelSelectionList->setVisible(false); + + mainLayout->addWidget(m_LabelSelectionList); + connect(m_LabelSelectionList, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged); + + this->OnRadioTransferAllClicked(true); + + Superclass::InitializeUI(mainLayout); +} + +void QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels) +{ + auto tool = this->GetConnectedToolAs(); + if (nullptr != tool) + { + mitk::SegWithPreviewTool::SelectedLabelVectorType labelIDs; + for (const auto& label : selectedLabels) + { + labelIDs.push_back(label->GetValue()); + } + + tool->SetSelectedLabels(labelIDs); + this->ActualizePreviewLabelVisibility(); + this->EnableWidgets(true); //used to actualize the ConfirmSeg btn via the delegate; + } +} + +void QmitkMultiLabelSegWithPreviewToolGUIBase::ActualizePreviewLabelVisibility() +{ + auto tool = this->GetConnectedToolAs(); + if (nullptr != tool) + { + auto preview = tool->GetPreviewSegmentation(); + if (nullptr != preview) + { + auto labelSet = preview->GetActiveLabelSet(); + auto selectedLabels = tool->GetSelectedLabels(); + + for (auto labelIter = labelSet->IteratorBegin(); labelIter != labelSet->IteratorEnd(); ++labelIter) + { + bool isVisible = tool->GetLabelTransferMode() == mitk::SegWithPreviewTool::LabelTransferMode::AllLabels + || (std::find(selectedLabels.begin(), selectedLabels.end(), labelIter->second->GetValue()) != selectedLabels.end()); + labelIter->second->SetVisible(isVisible); + labelSet->UpdateLookupTable(labelIter->second->GetValue()); + } + } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked(bool checked) +{ + m_LabelSelectionList->setVisible(!checked); + + auto tool = this->GetConnectedToolAs(); + if (nullptr != tool) + { + if (checked) + { + tool->SetLabelTransferMode(mitk::SegWithPreviewTool::LabelTransferMode::AllLabels); + } + else + { + tool->SetLabelTransferMode(mitk::SegWithPreviewTool::LabelTransferMode::SelectedLabels); + } + } + this->ActualizePreviewLabelVisibility(); +} + +void QmitkMultiLabelSegWithPreviewToolGUIBase::EnableWidgets(bool enabled) +{ + Superclass::EnableWidgets(enabled); + if (nullptr != m_LabelSelectionList) + { + m_LabelSelectionList->setEnabled(enabled); + } + + if (nullptr != m_RadioTransferAll) + { + m_RadioTransferAll->setEnabled(enabled); + } + + if (nullptr != m_RadioTransferSelected) + { + m_RadioTransferSelected->setEnabled(enabled); + } +} + +void QmitkMultiLabelSegWithPreviewToolGUIBase::SetLabelSetPreview(const mitk::LabelSetImage* preview) +{ + if (nullptr != m_LabelSelectionList) + { + m_LabelSelectionList->SetLabelSetImage(preview); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkAutoMLSegmentationToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h similarity index 62% rename from Modules/SegmentationUI/Qmitk/QmitkAutoMLSegmentationToolGUIBase.h rename to Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h index e4e48dc867..d6d5caaeb9 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAutoMLSegmentationToolGUIBase.h +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h @@ -1,52 +1,56 @@ /*============================================================================ 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 QmitkAutoMLSegmentationToolGUIBase_h_Included -#define QmitkAutoMLSegmentationToolGUIBase_h_Included +#ifndef QmitkMultiLabelSegWithPreviewToolGUIBase_h_Included +#define QmitkMultiLabelSegWithPreviewToolGUIBase_h_Included -#include "QmitkAutoSegmentationToolGUIBase.h" +#include "QmitkSegWithPreviewToolGUIBase.h" #include "QmitkSimpleLabelSetListWidget.h" #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for tools based on mitk::AutoMLSegmentationWithPreviewTool. This GUI offers an additional list to select the label that should be confirmed. */ -class MITKSEGMENTATIONUI_EXPORT QmitkAutoMLSegmentationToolGUIBase : public QmitkAutoSegmentationToolGUIBase +class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelSegWithPreviewToolGUIBase : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: - mitkClassMacro(QmitkAutoMLSegmentationToolGUIBase, QmitkAutoSegmentationToolGUIBase); + mitkClassMacro(QmitkMultiLabelSegWithPreviewToolGUIBase, QmitkSegWithPreviewToolGUIBase); protected slots : void OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels); + void OnRadioTransferAllClicked(bool checked); protected: - QmitkAutoMLSegmentationToolGUIBase(); - ~QmitkAutoMLSegmentationToolGUIBase() = default; + QmitkMultiLabelSegWithPreviewToolGUIBase(); + ~QmitkMultiLabelSegWithPreviewToolGUIBase() = default; void InitializeUI(QBoxLayout* mainLayout) override; void EnableWidgets(bool enabled) override; void SetLabelSetPreview(const mitk::LabelSetImage* preview); + void ActualizePreviewLabelVisibility(); private: QmitkSimpleLabelSetListWidget* m_LabelSelectionList = nullptr; + QWidget* m_RadioTransferAll = nullptr; + QWidget* m_RadioTransferSelected = nullptr; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp index 6533d2e726..e199373c53 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.cpp @@ -1,129 +1,143 @@ /*============================================================================ 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 "mitkOtsuTool3D.h" #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkOtsuTool3DGUI, "") -QmitkOtsuTool3DGUI::QmitkOtsuTool3DGUI() : QmitkAutoMLSegmentationToolGUIBase() +QmitkOtsuTool3DGUI::QmitkOtsuTool3DGUI() : QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc) { + auto enableMLSelectedDelegate = [this](bool enabled) + { + if (this->m_FirstPreviewComputation) + { + return false; + } + else + { + return this->m_SuperclassEnableConfirmSegBtnFnc(enabled); + } + }; + + m_EnableConfirmSegBtnFnc = enableMLSelectedDelegate; } -void QmitkOtsuTool3DGUI::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) +void QmitkOtsuTool3DGUI::ConnectNewTool(mitk::SegWithPreviewTool* newTool) { Superclass::ConnectNewTool(newTool); newTool->IsTimePointChangeAwareOff(); m_FirstPreviewComputation = true; } void QmitkOtsuTool3DGUI::InitializeUI(QBoxLayout* mainLayout) { m_Controls.setupUi(this); mainLayout->addLayout(m_Controls.verticalLayout); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked())); connect(m_Controls.m_Spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnRegionSpinboxChanged(int))); connect(m_Controls.advancedSettingsButton, SIGNAL(toggled(bool)), this, SLOT(OnAdvancedSettingsButtonToggled(bool))); this->OnAdvancedSettingsButtonToggled(false); Superclass::InitializeUI(mainLayout); } 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::OnAdvancedSettingsButtonToggled(bool toggled) { m_Controls.m_ValleyCheckbox->setVisible(toggled); m_Controls.binLabel->setVisible(toggled); m_Controls.m_BinsSpinBox->setVisible(toggled); auto tool = this->GetConnectedToolAs(); if (toggled && nullptr != tool) { int max = tool->GetMaxNumberOfBins(); if (max >= m_Controls.m_BinsSpinBox->minimum()) { m_Controls.m_BinsSpinBox->setMaximum(max); } } } void QmitkOtsuTool3DGUI::OnPreviewBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { if (!m_FirstPreviewComputation && (tool->GetNumberOfRegions() == static_cast(m_Controls.m_Spinbox->value()) && tool->GetUseValley() == m_Controls.m_ValleyCheckbox->isChecked() && tool->GetNumberOfBins() == static_cast(m_Controls.m_BinsSpinBox->value()))) return; m_FirstPreviewComputation = false; 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; } tool->SetNumberOfRegions(static_cast(m_Controls.m_Spinbox->value())); tool->SetUseValley(m_Controls.m_ValleyCheckbox->isChecked()); tool->SetNumberOfBins(static_cast(m_Controls.m_BinsSpinBox->value())); tool->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; } - this->SetLabelSetPreview(tool->GetMLPreview()); + this->SetLabelSetPreview(tool->GetPreviewSegmentation()); tool->IsTimePointChangeAwareOn(); + this->ActualizePreviewLabelVisibility(); } } void QmitkOtsuTool3DGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); m_Controls.m_ValleyCheckbox->setEnabled(enabled); m_Controls.binLabel->setEnabled(enabled); m_Controls.m_BinsSpinBox->setEnabled(enabled); m_Controls.previewButton->setEnabled(enabled); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h index 55b7030ea5..44078f4d61 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkOtsuTool3DGUI.h @@ -1,64 +1,65 @@ /*============================================================================ 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 "QmitkAutoMLSegmentationToolGUIBase.h" +#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h" #include "ui_QmitkOtsuToolWidgetControls.h" #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::OtsuTool3D. \sa mitk:: This GUI shows ... Last contributor: $Author$ */ -class MITKSEGMENTATIONUI_EXPORT QmitkOtsuTool3DGUI : public QmitkAutoMLSegmentationToolGUIBase +class MITKSEGMENTATIONUI_EXPORT QmitkOtsuTool3DGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase { Q_OBJECT public: - mitkClassMacro(QmitkOtsuTool3DGUI, QmitkAutoMLSegmentationToolGUIBase); + mitkClassMacro(QmitkOtsuTool3DGUI, QmitkMultiLabelSegWithPreviewToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected slots : void OnPreviewBtnClicked(); void OnRegionSpinboxChanged(int); private slots: void OnAdvancedSettingsButtonToggled(bool toggled); protected: QmitkOtsuTool3DGUI(); ~QmitkOtsuTool3DGUI() = default; - void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) override; + void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override; void InitializeUI(QBoxLayout* mainLayout) override; void EnableWidgets(bool enabled) override; Ui_QmitkOtsuToolWidgetControls m_Controls; bool m_FirstPreviewComputation = true; + EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.cpp index 603a58877e..35d0b0176b 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.cpp @@ -1,111 +1,111 @@ /*============================================================================ 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 "QmitkPickingToolGUI.h" #include #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkPickingToolGUI, "") -QmitkPickingToolGUI::QmitkPickingToolGUI() : QmitkAutoSegmentationToolGUIBase(false) +QmitkPickingToolGUI::QmitkPickingToolGUI() : QmitkSegWithPreviewToolGUIBase(false) { auto enablePickingDelegate = [this](bool enabled) { bool result = false; auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { result = enabled && tool->HasPicks(); } return result; }; m_EnableConfirmSegBtnFnc = enablePickingDelegate; } QmitkPickingToolGUI::~QmitkPickingToolGUI() { } void QmitkPickingToolGUI::OnResetPicksClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->ClearPicks(); } } void QmitkPickingToolGUI::OnRadioPickClicked(bool checked) { if (checked) { this->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Replace); this->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks); } else { this->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Merge); this->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); } } void QmitkPickingToolGUI::InitializeUI(QBoxLayout* mainLayout) { auto radioPick = new QRadioButton("Picking mode", this); radioPick->setToolTip("Pick certain parts of the label and dismiss the rest of the label content"); radioPick->setChecked(true); connect(radioPick, &QAbstractButton::toggled, this, &QmitkPickingToolGUI::OnRadioPickClicked); mainLayout->addWidget(radioPick); m_RadioPick = radioPick; this->OnRadioPickClicked(true); auto radioRelabel = new QRadioButton("Relabel mode", this); radioRelabel->setToolTip("Relabel certain parts of the segmentation as active label."); radioRelabel->setChecked(false); mainLayout->addWidget(radioRelabel); m_RadioRelabel = radioRelabel; QLabel* label = new QLabel("Press SHIFT and click to pick region(s).\nPress DEL to remove last pick.", this); mainLayout->addWidget(label); auto clearButton = new QPushButton("Reset picks",this); connect(clearButton, &QPushButton::clicked, this, &QmitkPickingToolGUI::OnResetPicksClicked); mainLayout->addWidget(clearButton); m_ClearPicksBtn = clearButton; Superclass::InitializeUI(mainLayout); } void QmitkPickingToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); if (nullptr != m_ClearPicksBtn) { m_ClearPicksBtn->setEnabled(enabled); } if (nullptr != m_RadioPick) { m_RadioPick->setEnabled(enabled); } if (nullptr != m_RadioRelabel) { m_RadioRelabel->setEnabled(enabled); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.h index 2d00cb3b78..46c99cc6ad 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkPickingToolGUI.h @@ -1,50 +1,50 @@ /*============================================================================ 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 QmitkPickingToolGUI_h_Included #define QmitkPickingToolGUI_h_Included -#include "QmitkAutoSegmentationToolGUIBase.h" +#include "QmitkSegWithPreviewToolGUIBase.h" #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::PickingTool. \sa mitk::PickingTool */ -class MITKSEGMENTATIONUI_EXPORT QmitkPickingToolGUI : public QmitkAutoSegmentationToolGUIBase +class MITKSEGMENTATIONUI_EXPORT QmitkPickingToolGUI : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: - mitkClassMacro(QmitkPickingToolGUI, QmitkAutoSegmentationToolGUIBase); + mitkClassMacro(QmitkPickingToolGUI, QmitkSegWithPreviewToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected slots : void OnResetPicksClicked(); void OnRadioPickClicked(bool checked); protected: QmitkPickingToolGUI(); ~QmitkPickingToolGUI() override; void InitializeUI(QBoxLayout* mainLayout) override; void EnableWidgets(bool enabled) override; private: QWidget* m_ClearPicksBtn = nullptr; QWidget* m_RadioPick = nullptr; QWidget* m_RadioRelabel = nullptr; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkAutoSegmentationToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp similarity index 64% rename from Modules/SegmentationUI/Qmitk/QmitkAutoSegmentationToolGUIBase.cpp rename to Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp index f4a276d6ff..03b8b4851f 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAutoSegmentationToolGUIBase.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp @@ -1,210 +1,186 @@ /*============================================================================ 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 "QmitkAutoSegmentationToolGUIBase.h" +#include "QmitkSegWithPreviewToolGUIBase.h" #include #include #include #include #include bool DefaultEnableConfirmSegBtnFunction(bool enabled) { return enabled; } -QmitkAutoSegmentationToolGUIBase::QmitkAutoSegmentationToolGUIBase(bool mode2D) : QmitkToolGUI(), m_EnableConfirmSegBtnFnc(DefaultEnableConfirmSegBtnFunction), m_Mode2D(mode2D) +QmitkSegWithPreviewToolGUIBase::QmitkSegWithPreviewToolGUIBase(bool mode2D) : QmitkToolGUI(), m_EnableConfirmSegBtnFnc(DefaultEnableConfirmSegBtnFunction), m_Mode2D(mode2D) { connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } -QmitkAutoSegmentationToolGUIBase::~QmitkAutoSegmentationToolGUIBase() +QmitkSegWithPreviewToolGUIBase::~QmitkSegWithPreviewToolGUIBase() { if (m_Tool.IsNotNull()) { - m_Tool->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkAutoSegmentationToolGUIBase::BusyStateChanged); + m_Tool->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged); } } -void QmitkAutoSegmentationToolGUIBase::OnNewToolAssociated(mitk::Tool *tool) +void QmitkSegWithPreviewToolGUIBase::OnNewToolAssociated(mitk::Tool *tool) { if (m_Tool.IsNotNull()) { this->DisconnectOldTool(m_Tool); } - m_Tool = dynamic_cast(tool); + m_Tool = dynamic_cast(tool); if (nullptr == m_MainLayout) { // create the visible widgets m_MainLayout = new QVBoxLayout(this); m_ConfirmSegBtn = new QPushButton("Confirm Segmentation", this); connect(m_ConfirmSegBtn, SIGNAL(clicked()), this, SLOT(OnAcceptPreview())); m_CheckIgnoreLocks = new QCheckBox("Ignore label locks", this); m_CheckIgnoreLocks->setChecked(m_Tool->GetOverwriteStyle() == mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); m_CheckIgnoreLocks->setToolTip("If checked, the lock state of labels will be ignored when the preview segmentation is confermed. Thus also locked label pixels can be changed by the operation."); m_CheckMerge = new QCheckBox("Merge with existing content", this); m_CheckMerge->setChecked(m_Tool->GetMergeStyle()==mitk::MultiLabelSegmentation::MergeStyle::Merge); m_CheckMerge->setToolTip("If checked, the preview segmantation will be merged with the existing segmantation into a union. If unchecked, the preview content will replace the old segmantation"); m_CheckProcessAll = new QCheckBox("Process all time steps", this); m_CheckProcessAll->setChecked(false); m_CheckProcessAll->setToolTip("Process all time steps of the dynamic segmentation and not just the currently visible time step."); m_CheckProcessAll->setVisible(!m_Mode2D); //remark: keept m_CheckProcessAll deactivated in 2D because in this refactoring //it should be kept to the status quo and it was not clear how interpolation //would behave. As soon as it is sorted out we can remove that "feature switch" //or the comment. - m_CheckCreateNew = new QCheckBox("Create as new segmentation", this); - m_CheckCreateNew->setChecked(false); - m_CheckCreateNew->setToolTip("Add the confirmed segmentation as a new segmentation instead of overwriting the currently selected."); - m_CheckCreateNew->setVisible(!m_Mode2D); - //remark: keept m_CheckCreateNew deactivated in 2D because in this refactoring - //it should be kept to the status quo and it was not clear how interpolation - //would behave. As soon as it is sorted out we can remove that "feature switch" - //or the comment. - this->InitializeUI(m_MainLayout); m_MainLayout->addWidget(m_ConfirmSegBtn); m_MainLayout->addWidget(m_CheckIgnoreLocks); m_MainLayout->addWidget(m_CheckMerge); m_MainLayout->addWidget(m_CheckProcessAll); - m_MainLayout->addWidget(m_CheckCreateNew); } if (m_Tool.IsNotNull()) { this->ConnectNewTool(m_Tool); } } -void QmitkAutoSegmentationToolGUIBase::OnAcceptPreview() +void QmitkSegWithPreviewToolGUIBase::OnAcceptPreview() { if (m_Tool.IsNotNull()) { if (m_CheckIgnoreLocks->isChecked()) { m_Tool->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); } else { m_Tool->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks); } if (m_CheckMerge->isChecked()) { m_Tool->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Merge); } else { m_Tool->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Replace); } - if (m_CheckCreateNew->isChecked()) - { - m_Tool->SetOverwriteExistingSegmentation(false); - } - else - { - m_Tool->SetOverwriteExistingSegmentation(true); - } - m_Tool->SetCreateAllTimeSteps(m_CheckProcessAll->isChecked()); m_ConfirmSegBtn->setEnabled(false); m_Tool->ConfirmSegmentation(); } } -void QmitkAutoSegmentationToolGUIBase::DisconnectOldTool(mitk::AutoSegmentationWithPreviewTool* oldTool) +void QmitkSegWithPreviewToolGUIBase::DisconnectOldTool(mitk::SegWithPreviewTool* oldTool) { - oldTool->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkAutoSegmentationToolGUIBase::BusyStateChanged); + oldTool->CurrentlyBusy -= mitk::MessageDelegate1(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged); } -void QmitkAutoSegmentationToolGUIBase::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) +void QmitkSegWithPreviewToolGUIBase::ConnectNewTool(mitk::SegWithPreviewTool* newTool) { newTool->CurrentlyBusy += - mitk::MessageDelegate1(this, &QmitkAutoSegmentationToolGUIBase::BusyStateChanged); + mitk::MessageDelegate1(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged); - newTool->SetOverwriteExistingSegmentation(true); m_CheckProcessAll->setVisible(newTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps() > 1); this->EnableWidgets(true); } -void QmitkAutoSegmentationToolGUIBase::InitializeUI(QBoxLayout* /*mainLayout*/) +void QmitkSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* /*mainLayout*/) { //default implementation does nothing } -void QmitkAutoSegmentationToolGUIBase::BusyStateChanged(bool isBusy) +void QmitkSegWithPreviewToolGUIBase::BusyStateChanged(bool isBusy) { if (isBusy) { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); } else { QApplication::restoreOverrideCursor(); } this->EnableWidgets(!isBusy); } -void QmitkAutoSegmentationToolGUIBase::EnableWidgets(bool enabled) +void QmitkSegWithPreviewToolGUIBase::EnableWidgets(bool enabled) { if (nullptr != m_MainLayout) { if (nullptr != m_ConfirmSegBtn) { m_ConfirmSegBtn->setEnabled(m_EnableConfirmSegBtnFnc(enabled)); } if (nullptr != m_CheckIgnoreLocks) { m_CheckIgnoreLocks->setEnabled(enabled); } if (nullptr != m_CheckMerge) { m_CheckMerge->setEnabled(enabled); } if (nullptr != m_CheckProcessAll) { m_CheckProcessAll->setEnabled(enabled); } - if (nullptr != m_CheckCreateNew) - { - m_CheckCreateNew->setEnabled(enabled); - } } } -void QmitkAutoSegmentationToolGUIBase::SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle) +void QmitkSegWithPreviewToolGUIBase::SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle) { if (nullptr != m_CheckMerge) { m_CheckMerge->setChecked(mergeStyle == mitk::MultiLabelSegmentation::MergeStyle::Merge); } }; -void QmitkAutoSegmentationToolGUIBase::SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) +void QmitkSegWithPreviewToolGUIBase::SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) { if (nullptr != m_CheckIgnoreLocks) { m_CheckIgnoreLocks->setChecked(overwriteStyle == mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); } }; diff --git a/Modules/SegmentationUI/Qmitk/QmitkAutoSegmentationToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h similarity index 78% rename from Modules/SegmentationUI/Qmitk/QmitkAutoSegmentationToolGUIBase.h rename to Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h index 54701e27ca..6f5f0bedee 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAutoSegmentationToolGUIBase.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h @@ -1,96 +1,95 @@ /*============================================================================ 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 QmitkAutoSegmentationToolGUIBase_h_Included -#define QmitkAutoSegmentationToolGUIBase_h_Included +#ifndef QmitkSegWithPreviewToolGUIBase_h_Included +#define QmitkSegWithPreviewToolGUIBase_h_Included #include "QmitkToolGUI.h" -#include "mitkAutoSegmentationWithPreviewTool.h" +#include "mitkSegWithPreviewTool.h" #include class QCheckBox; class QPushButton; class QBoxLayout; /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI base clase for tools derived from mitk::AutoSegmentationTool. */ -class MITKSEGMENTATIONUI_EXPORT QmitkAutoSegmentationToolGUIBase : public QmitkToolGUI +class MITKSEGMENTATIONUI_EXPORT QmitkSegWithPreviewToolGUIBase : public QmitkToolGUI { Q_OBJECT public: - mitkClassMacro(QmitkAutoSegmentationToolGUIBase, QmitkToolGUI); + mitkClassMacro(QmitkSegWithPreviewToolGUIBase, QmitkToolGUI); itkCloneMacro(Self); itkGetConstMacro(Mode2D, bool); protected slots: void OnNewToolAssociated(mitk::Tool *); void OnAcceptPreview(); protected: - QmitkAutoSegmentationToolGUIBase(bool mode2D); - ~QmitkAutoSegmentationToolGUIBase() override; + QmitkSegWithPreviewToolGUIBase(bool mode2D); + ~QmitkSegWithPreviewToolGUIBase() override; - virtual void DisconnectOldTool(mitk::AutoSegmentationWithPreviewTool* oldTool); - virtual void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool); + virtual void DisconnectOldTool(mitk::SegWithPreviewTool* oldTool); + virtual void ConnectNewTool(mitk::SegWithPreviewTool* newTool); /**This method is called by OnNewToolAssociated if the UI is initialized the first time to allow derived classes to introduce own UI code. Overwrite to change. The implementation should ensure that alle widgets needed for the tool UI are properly allocated. If one needs to eecute time (e.g. to connect events between the tool and the UI) each time the tool changes, override the functions ConnectNewTool() and DisconnectOldTool().*/ virtual void InitializeUI(QBoxLayout* mainLayout); void BusyStateChanged(bool isBusy) override; using EnableConfirmSegBtnFunctionType = std::function; EnableConfirmSegBtnFunctionType m_EnableConfirmSegBtnFnc; /**This method is used to control/set the enabled state of the tool UI widgets. It is e.g. used if the busy state is changed (see BusyStateChanged). Override the default implmentation, e.g. if a tool adds his own UI elements (normally by overriding InitializeUI()) and wants to control how the widgets are enabled/disabled.*/ virtual void EnableWidgets(bool enabled); template TTool* GetConnectedToolAs() { return dynamic_cast(m_Tool.GetPointer()); }; void SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle); void SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle); private: QCheckBox* m_CheckIgnoreLocks = nullptr; QCheckBox* m_CheckMerge = nullptr; QCheckBox* m_CheckProcessAll = nullptr; - QCheckBox* m_CheckCreateNew = nullptr; QPushButton* m_ConfirmSegBtn = nullptr; QBoxLayout* m_MainLayout = nullptr; /**Indicates if the tool is in 2D or 3D mode.*/ bool m_Mode2D; - mitk::AutoSegmentationWithPreviewTool::Pointer m_Tool; + mitk::SegWithPreviewTool::Pointer m_Tool; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp index 8d6f3215f9..e0be0aa079 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.cpp @@ -1,182 +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 "QmitkSimpleLabelSetListWidget.h" #include "mitkMessage.h" #include -QmitkSimpleLabelSetListWidget::QmitkSimpleLabelSetListWidget(QWidget* parent) : QWidget(parent), m_LabelList(nullptr) +QmitkSimpleLabelSetListWidget::QmitkSimpleLabelSetListWidget(QWidget* parent) : QWidget(parent), m_LabelList(nullptr), m_Emmiting(false) { 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()); + if (!this->m_Emmiting) + { + this->ResetList(); + + this->m_Emmiting = true; + emit ActiveLayerChanged(); + emit SelectedLabelsChanged(this->SelectedLabels()); + this->m_Emmiting = false; + } } void QmitkSimpleLabelSetListWidget::OnLabelChanged() { - this->ResetList(); - emit ActiveLayerChanged(); - emit SelectedLabelsChanged(this->SelectedLabels()); + if (!this->m_Emmiting) + { + this->ResetList(); + + this->m_Emmiting = true; + emit ActiveLayerChanged(); + emit SelectedLabelsChanged(this->SelectedLabels()); + this->m_Emmiting = false; + } } void QmitkSimpleLabelSetListWidget::OnLabelSelectionChanged() { - emit SelectedLabelsChanged(this->SelectedLabels()); + if (!this->m_Emmiting) + { + this->m_Emmiting = true; + emit SelectedLabelsChanged(this->SelectedLabels()); + this->m_Emmiting = false; + } } 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 index 88c716ff0c..727be32c0e 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSimpleLabelSetListWidget.h @@ -1,63 +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 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; + bool m_Emmiting; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp index 29114ac8f8..2f39408c48 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp @@ -1,588 +1,588 @@ /*============================================================================ 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 "QmitknnUNetToolGUI.h" #include "mitkProcessExecutor.h" #include "mitknnUnetTool.h" #include #include #include #include #include #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitknnUNetToolGUI, "") -QmitknnUNetToolGUI::QmitknnUNetToolGUI() : QmitkAutoMLSegmentationToolGUIBase() +QmitknnUNetToolGUI::QmitknnUNetToolGUI() : QmitkMultiLabelSegWithPreviewToolGUIBase() { // Nvidia-smi command returning zero doesn't always imply lack of GPUs. // Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given. if (m_GpuLoader.GetGPUCount() == 0) { std::string warning = "WARNING: No GPUs were detected on your machine. The nnUNet tool might not work."; ShowErrorMessage(warning); } // define predicates for multi modal data selection combobox auto imageType = mitk::TNodePredicateDataType::New(); auto labelSetImageType = mitk::NodePredicateNot::New(mitk::TNodePredicateDataType::New()); m_MultiModalPredicate = mitk::NodePredicateAnd::New(imageType, labelSetImageType).GetPointer(); } -void QmitknnUNetToolGUI::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) +void QmitknnUNetToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool) { Superclass::ConnectNewTool(newTool); newTool->IsTimePointChangeAwareOff(); } void QmitknnUNetToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); #ifndef _WIN32 m_Controls.pythonEnvComboBox->addItem("/usr/bin"); #endif m_Controls.pythonEnvComboBox->addItem("Select"); AutoParsePythonPaths(); SetGPUInfo(); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewRequested())); connect(m_Controls.modeldirectoryBox, SIGNAL(directoryChanged(const QString &)), this, SLOT(OnDirectoryChanged(const QString &))); connect( m_Controls.modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect(m_Controls.taskBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTaskChanged(const QString &))); connect( m_Controls.plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); connect(m_Controls.multiModalBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int))); connect(m_Controls.multiModalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnModalitiesNumberChanged(int))); connect(m_Controls.pythonEnvComboBox, #if QT_VERSION >= 0x050F00 // 5.15 SIGNAL(textActivated(const QString &)), #elif QT_VERSION >= 0x050C00 // 5.12 SIGNAL(currentTextChanged(const QString &)), #endif this, SLOT(OnPythonPathChanged(const QString &))); connect(m_Controls.refreshdirectoryBox, SIGNAL(clicked()), this, SLOT(OnRefreshPresssed())); connect(m_Controls.clearCacheButton, SIGNAL(clicked()), this, SLOT(OnClearCachePressed())); m_Controls.multiModalSpinBox->setVisible(false); m_Controls.multiModalSpinBox->setEnabled(false); m_Controls.multiModalSpinLabel->setVisible(false); m_Controls.previewButton->setEnabled(false); QIcon refreshIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg")); m_Controls.refreshdirectoryBox->setIcon(refreshIcon); QIcon dirIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")); m_Controls.modeldirectoryBox->setIcon(dirIcon); m_Controls.refreshdirectoryBox->setEnabled(true); m_Controls.statusLabel->setTextFormat(Qt::RichText); if (m_GpuLoader.GetGPUCount() != 0) { WriteStatusMessage(QString("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.")); } else { WriteErrorMessage(QString("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.")); } mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); m_UI_ROWS = m_Controls.advancedSettingsLayout->rowCount(); // Must do. Row count is correct only here. DisableEverything(); QString lastSelectedPyEnv = m_Settings.value("nnUNet/LastPythonPath").toString(); m_Controls.pythonEnvComboBox->setCurrentText(lastSelectedPyEnv); } void QmitknnUNetToolGUI::OnPreviewRequested() { mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); if (nullptr != tool) { QString pythonPathTextItem = ""; try { size_t hashKey(0); m_Controls.previewButton->setEnabled(false); // To prevent misclicked back2back prediction. qApp->processEvents(); tool->PredictOn(); // purposefully placed to make tool->GetMTime different than before. QString modelName = m_Controls.modelBox->currentText(); if (modelName.startsWith("ensemble", Qt::CaseInsensitive)) { ProcessEnsembleModelsParams(tool); } else { ProcessModelParams(tool); } pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText(); QString pythonPath = m_PythonPath; if (!IsNNUNetInstalled(pythonPath)) { throw std::runtime_error("nnUNet is not detected in the selected python environment. Please select a valid " "python environment or install nnUNet."); } tool->SetPythonPath(pythonPath.toStdString()); tool->SetModelDirectory(m_ParentFolder->getResultsFolder().toStdString()); // checkboxes tool->SetMirror(m_Controls.mirrorBox->isChecked()); tool->SetMixedPrecision(m_Controls.mixedPrecisionBox->isChecked()); tool->SetNoPip(false); bool doCache = m_Controls.enableCachingCheckBox->isChecked(); // Spinboxes tool->SetGpuId(FetchSelectedGPUFromUI()); // Multi-Modal tool->MultiModalOff(); if (m_Controls.multiModalBox->isChecked()) { if (m_Controls.multiModalSpinBox->value() > 0) { tool->m_OtherModalPaths.clear(); tool->m_OtherModalPaths = FetchMultiModalImagesFromUI(); tool->MultiModalOn(); } else { throw std::runtime_error("Please select more than one modalities for a multi-modal task. If you " "would like to use only one modality then uncheck the Multi-Modal option."); } } if (doCache) { hashKey = nnUNetCache::GetUniqueHash(tool->m_ParamQ); if (m_Cache.contains(hashKey)) { tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. } } if (tool->GetPredict()) { tool->m_InputBuffer = nullptr; WriteStatusMessage(QString("STATUS: Starting Segmentation task... This might take a while.")); tool->UpdatePreview(); if (nullptr == tool->GetOutputBuffer()) { SegmentationProcessFailed(); } else { SegmentationResultHandler(tool); if (doCache) { - AddToCache(hashKey, tool->GetMLPreview()); + AddToCache(hashKey, tool->GetPreviewSegmentation()); } } tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. } else { MITK_INFO << "won't do segmentation. Key found: " << QString::number(hashKey).toStdString(); if (m_Cache.contains(hashKey)) { nnUNetCache *cacheObject = m_Cache[hashKey]; MITK_INFO << "fetched pointer " << cacheObject->m_SegCache.GetPointer(); tool->SetOutputBuffer(const_cast(cacheObject->m_SegCache.GetPointer())); SegmentationResultHandler(tool, true); } } m_Controls.previewButton->setEnabled(true); } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for nnUNet segmentation. Reason: " << e.what(); ShowErrorMessage(errorMsg.str()); WriteErrorMessage(QString::fromStdString(errorMsg.str())); m_Controls.previewButton->setEnabled(true); tool->PredictOff(); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation nnUNet segmentation."; ShowErrorMessage(errorMsg); m_Controls.previewButton->setEnabled(true); tool->PredictOff(); return; } if (!pythonPathTextItem.isEmpty()) { // only cache if the prediction ended without errors. m_Settings.setValue("nnUNet/LastPythonPath", pythonPathTextItem); } } } std::vector QmitknnUNetToolGUI::FetchMultiModalImagesFromUI() { std::vector modals; if (m_Controls.multiModalBox->isChecked() && !m_Modalities.empty()) { std::set nodeNames; // set container for keeping names of all nodes to check if they are added twice. for (QmitkDataStorageComboBox *modality : m_Modalities) { if (nodeNames.find(modality->GetSelectedNode()->GetName()) == nodeNames.end()) { modals.push_back(dynamic_cast(modality->GetSelectedNode()->GetData())); nodeNames.insert(modality->GetSelectedNode()->GetName()); } else { throw std::runtime_error("Same modality is selected more than once. Please change your selection."); break; } } } return modals; } bool QmitknnUNetToolGUI::IsNNUNetInstalled(const QString &pythonPath) { QString fullPath = pythonPath; #ifdef _WIN32 if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("Scripts"); } #else if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("bin"); } #endif fullPath = fullPath.mid(fullPath.indexOf(" ") + 1); bool isExists = QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")) && QFile::exists(fullPath + QDir::separator() + QString("python3")); return isExists; } void QmitknnUNetToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str()); messageBox->exec(); delete messageBox; MITK_WARN << message; } void QmitknnUNetToolGUI::WriteStatusMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); } void QmitknnUNetToolGUI::WriteErrorMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); } void QmitknnUNetToolGUI::ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer tool) { if (m_EnsembleParams[0]->modelBox->currentText() == m_EnsembleParams[1]->modelBox->currentText()) { throw std::runtime_error("Both models you have selected for ensembling are the same."); } QString taskName = m_Controls.taskBox->currentText(); bool isPPJson = m_Controls.postProcessingCheckBox->isChecked(); std::vector requestQ; QString ppDirFolderNamePart1 = "ensemble_"; QStringList ppDirFolderNameParts; for (auto &layout : m_EnsembleParams) { QStringList ppDirFolderName; QString modelName = layout->modelBox->currentText(); ppDirFolderName << modelName; ppDirFolderName << "__"; QString trainer = layout->trainerBox->currentText(); ppDirFolderName << trainer; ppDirFolderName << "__"; QString planId = layout->plannerBox->currentText(); ppDirFolderName << planId; if (!IsModelExists(modelName, taskName, QString(trainer + "__" + planId))) { std::string errorMsg = "The configuration " + modelName.toStdString() + " you have selected doesn't exist. Check your Results Folder again."; throw std::runtime_error(errorMsg); } std::vector testfold = FetchSelectedFoldsFromUI(layout->foldBox); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, testfold); requestQ.push_back(modelObject); ppDirFolderNameParts << ppDirFolderName.join(QString("")); } tool->EnsembleOn(); if (isPPJson) { QString ppJsonFilePossibility1 = QDir::cleanPath( m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.first() + "--" + ppDirFolderNameParts.last() + QDir::separator() + "postprocessing.json"); QString ppJsonFilePossibility2 = QDir::cleanPath( m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.last() + "--" + ppDirFolderNameParts.first() + QDir::separator() + "postprocessing.json"); if (QFile(ppJsonFilePossibility1).exists()) { tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility1.toStdString()); const QString statusMsg = "Post Processing JSON file found: " + ppJsonFilePossibility1; WriteStatusMessage(statusMsg); } else if (QFile(ppJsonFilePossibility2).exists()) { tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility2.toStdString()); const QString statusMsg = "Post Processing JSON file found:" + ppJsonFilePossibility2; WriteStatusMessage(statusMsg); } else { std::string errorMsg = "No post processing file was found for the selected ensemble combination. Continuing anyway..."; ShowErrorMessage(errorMsg); } } tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; } void QmitknnUNetToolGUI::ProcessModelParams(mitk::nnUNetTool::Pointer tool) { tool->EnsembleOff(); std::vector requestQ; QString modelName = m_Controls.modelBox->currentText(); QString taskName = m_Controls.taskBox->currentText(); QString trainer = m_Controls.trainerBox->currentText(); QString planId = m_Controls.plannerBox->currentText(); std::vector fetchedFolds = FetchSelectedFoldsFromUI(m_Controls.foldBox); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, fetchedFolds); requestQ.push_back(modelObject); tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; } bool QmitknnUNetToolGUI::IsModelExists(const QString &modelName, const QString &taskName, const QString &trainerPlanner) { QString modelSearchPath = QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + modelName + QDir::separator() + taskName + QDir::separator() + trainerPlanner); if (QDir(modelSearchPath).exists()) { return true; } return false; } void QmitknnUNetToolGUI::CheckAllInCheckableComboBox(ctkCheckableComboBox *foldBox) { // Recalling all added items to check-mark it. const QAbstractItemModel *qaim = foldBox->checkableModel(); auto rows = qaim->rowCount(); for (std::remove_const_t i = 0; i < rows; ++i) { const QModelIndex mi = qaim->index(i, 0); foldBox->setCheckState(mi, Qt::Checked); } } std::pair QmitknnUNetToolGUI::ExtractTrainerPlannerFromString(QStringList trainerPlanners) { QString splitterString = "__"; QStringList trainers, planners; for (const auto &trainerPlanner : trainerPlanners) { trainers << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).first(); planners << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).last(); } trainers.removeDuplicates(); planners.removeDuplicates(); return std::make_pair(trainers, planners); } std::vector QmitknnUNetToolGUI::FetchSelectedFoldsFromUI(ctkCheckableComboBox *foldBox) { std::vector folds; if (foldBox->noneChecked()) { CheckAllInCheckableComboBox(foldBox); } QModelIndexList foldList = foldBox->checkedIndexes(); for (const auto &index : foldList) { QString foldQString = foldBox->itemText(index.row()).split("_", QString::SplitBehavior::SkipEmptyParts).last(); folds.push_back(foldQString.toStdString()); } return folds; } void QmitknnUNetToolGUI::OnClearCachePressed() { m_Cache.clear(); UpdateCacheCountOnUI(); } void QmitknnUNetToolGUI::UpdateCacheCountOnUI() { QString cacheText = m_CACHE_COUNT_BASE_LABEL + QString::number(m_Cache.size()); m_Controls.cacheCountLabel->setText(cacheText); } void QmitknnUNetToolGUI::AddToCache(size_t &hashKey, mitk::LabelSetImage::ConstPointer mlPreview) { nnUNetCache *newCacheObj = new nnUNetCache; newCacheObj->m_SegCache = mlPreview; m_Cache.insert(hashKey, newCacheObj); MITK_INFO << "New hash: " << hashKey << " " << newCacheObj->m_SegCache.GetPointer(); UpdateCacheCountOnUI(); } void QmitknnUNetToolGUI::SetGPUInfo() { std::vector specs = m_GpuLoader.GetAllGPUSpecs(); for (const QmitkGPUSpec &gpuSpec : specs) { m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")"); } if (specs.empty()) { m_Controls.gpuComboBox->setEditable(true); m_Controls.gpuComboBox->addItem(QString::number(0)); m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this)); } } unsigned int QmitknnUNetToolGUI::FetchSelectedGPUFromUI() { QString gpuInfo = m_Controls.gpuComboBox->currentText(); if (m_GpuLoader.GetGPUCount() == 0) { return static_cast(gpuInfo.toInt()); } else { QString gpuId = gpuInfo.split(":", QString::SplitBehavior::SkipEmptyParts).first(); return static_cast(gpuId.toInt()); } } QString QmitknnUNetToolGUI::FetchResultsFolderFromEnv() { const char *pathVal = itksys::SystemTools::GetEnv("RESULTS_FOLDER"); QString retVal; if (pathVal) { retVal = QString::fromUtf8(pathVal); } else { retVal = m_Settings.value("nnUNet/LastRESULTS_FOLDERPath").toString(); } return retVal; } QString QmitknnUNetToolGUI::DumpJSONfromPickle(const QString &parentPath) { const QString picklePath = parentPath + QDir::separator() + QString("plans.pkl"); const QString jsonPath = parentPath + QDir::separator() + QString("mitk_export.json"); if (!QFile::exists(jsonPath)) { mitk::ProcessExecutor::Pointer spExec = mitk::ProcessExecutor::New(); itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New(); mitk::ProcessExecutor::ArgumentListType args; args.push_back("-c"); std::string pythonCode; // python syntax to parse plans.pkl file and export as Json file. pythonCode.append("import pickle;"); pythonCode.append("import json;"); pythonCode.append("loaded_pickle = pickle.load(open('"); pythonCode.append(picklePath.toStdString()); pythonCode.append("','rb'));"); pythonCode.append("modal_dict = {key: loaded_pickle[key] for key in loaded_pickle.keys() if key in " "['modalities','num_modalities']};"); pythonCode.append("json.dump(modal_dict, open('"); pythonCode.append(jsonPath.toStdString()); pythonCode.append("', 'w'))"); args.push_back(pythonCode); try { spExec->Execute(m_PythonPath.toStdString(), "python3", args); } catch (const mitk::Exception &e) { MITK_ERROR << "Pickle parsing FAILED!" << e.GetDescription(); // SHOW ERROR WriteStatusMessage( "Parsing failed in backend. Multiple Modalities will now have to be manually entered by the user."); return QString(""); } } return jsonPath; } void QmitknnUNetToolGUI::DisplayMultiModalInfoFromJSON(const QString &jsonPath) { if (QFile::exists(jsonPath)) { QFile file(jsonPath); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QByteArray bytes = file.readAll(); file.close(); QJsonParseError jsonError; QJsonDocument document = QJsonDocument::fromJson(bytes, &jsonError); if (jsonError.error != QJsonParseError::NoError) { MITK_INFO << "fromJson failed: " << jsonError.errorString().toStdString() << endl; return; } if (document.isObject()) { QJsonObject jsonObj = document.object(); int num_mods = jsonObj["num_modalities"].toInt(); ClearAllModalLabels(); if (num_mods > 1) { m_Controls.multiModalBox->setChecked(true); m_Controls.multiModalBox->setEnabled(false); m_Controls.multiModalSpinBox->setValue(num_mods - 1); m_Controls.advancedSettingsLayout->update(); QJsonObject obj = jsonObj.value("modalities").toObject(); QStringList keys = obj.keys(); int count = 0; for (auto key : keys) { auto value = obj.take(key); QLabel *label = new QLabel("" + value.toString() + "", this); m_ModalLabels.push_back(label); m_Controls.advancedSettingsLayout->addWidget(label, m_UI_ROWS + 1 + count, 0); count++; } m_Controls.multiModalSpinBox->setMinimum(num_mods - 1); m_Controls.advancedSettingsLayout->update(); } else { m_Controls.multiModalSpinBox->setMinimum(0); m_Controls.multiModalBox->setChecked(false); } } } } } diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h index 601f36453e..a2836af189 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h @@ -1,342 +1,342 @@ /*============================================================================ 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.s ============================================================================*/ #ifndef QmitknnUNetToolGUI_h_Included #define QmitknnUNetToolGUI_h_Included -#include "QmitkAutoMLSegmentationToolGUIBase.h" +#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h" #include "QmitknnUNetFolderParser.h" #include "QmitknnUNetGPU.h" #include "mitknnUnetTool.h" #include "ui_QmitknnUNetToolGUIControls.h" #include #include #include #include #include #include #include class nnUNetCache { public: mitk::LabelSetImage::ConstPointer m_SegCache; static size_t GetUniqueHash(std::vector &requestQ) { size_t hashCode = 0; for (mitk::ModelParams &request : requestQ) { boost::hash_combine(hashCode, request.generateHash()); } return hashCode; } }; -class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkAutoMLSegmentationToolGUIBase +class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase { Q_OBJECT public: - mitkClassMacro(QmitknnUNetToolGUI, QmitkAutoMLSegmentationToolGUIBase); + mitkClassMacro(QmitknnUNetToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); QCache m_Cache; protected slots: /** * @brief Qt slot * */ void OnPreviewRequested(); /** * @brief Qt slot * */ void OnDirectoryChanged(const QString &); /** * @brief Qt slot * */ void OnModelChanged(const QString &); /** * @brief Qt slot * */ void OnTaskChanged(const QString &); /** * @brief Qt slot * */ void OnTrainerChanged(const QString &); /** * @brief Qt slot * */ void OnCheckBoxChanged(int); /** * @brief Qthread slot to capture failures from thread worker and * shows error message * */ void SegmentationProcessFailed(); /** * @brief Qthread to capture sucessfull nnUNet segmentation. * Further, renders the LabelSet image */ void SegmentationResultHandler(mitk::nnUNetTool *, bool forceRender = false); /** * @brief Qt Slot * */ void OnModalitiesNumberChanged(int); /** * @brief Qt Slot * */ void OnPythonPathChanged(const QString &); /** * @brief Qt slot * */ void OnRefreshPresssed(); /** * @brief Qt slot * */ void OnClearCachePressed(); protected: QmitknnUNetToolGUI(); ~QmitknnUNetToolGUI() = default; - void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) override; + void ConnectNewTool(mitk::SegWithPreviewTool *newTool) override; void InitializeUI(QBoxLayout *mainLayout) override; void EnableWidgets(bool enabled) override; private: /** * @brief Clears all displayed modal labels and widgets from GUI. * */ void ClearAllModalities(); /** * @brief Parses Json file containing modality info and populates * labels and selection widgets accordingly on the GUI. */ void DisplayMultiModalInfoFromJSON(const QString&); /** * @brief Clears all modality labels previously populated from GUI * */ void ClearAllModalLabels(); /** * @brief Runs a set of python commands to read "plans.pkl" and extract * modality information required for inferencing. This information is exported * as json file : "mitk_export.json". * * @return QString */ QString DumpJSONfromPickle(const QString&); /** * @brief Searches RESULTS_FOLDER environment variable. If not found, * returns from the QSettings stored last used path value. * @return QString */ QString FetchResultsFolderFromEnv(); /** * @brief Returns GPU id of the selected GPU from the Combo box. * * @return unsigned int */ unsigned int FetchSelectedGPUFromUI(); /** * @brief Adds GPU information to the gpu combo box. * In case, there aren't any GPUs avaialble, the combo box will be * rendered editable. */ void SetGPUInfo(); /** * @brief Inserts the hash and segmentation into cache and * updates count on UI. */ void AddToCache(size_t&, mitk::LabelSetImage::ConstPointer); /** * @brief Checks all the entries of the ctkCheckableComboBox ui widget. * This feature is not present in ctkCheckableComboBox API. */ void CheckAllInCheckableComboBox(ctkCheckableComboBox *); /** * @brief Parses the folder names containing trainer and planner together and, * returns it as separate lists. * @return std::pair */ std::pair ExtractTrainerPlannerFromString(QStringList); /** * @brief Parses the ensemble UI elements and sets to nnUNetTool object pointer. * */ void ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer); /** * @brief Parses the UI elements and sets to nnUNetTool object pointer. * */ void ProcessModelParams(mitk::nnUNetTool::Pointer); /** * @brief Creates and renders QmitknnUNetTaskParamsUITemplate layout for ensemble input. */ void ShowEnsembleLayout(bool visible = true); /** * @brief Creates a QMessage object and shows on screen. */ void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical); /** * @brief Writes any message in white on the tool pane. */ void WriteStatusMessage(const QString &); /** * @brief Writes any message in red on the tool pane. */ void WriteErrorMessage(const QString &); /** * @brief Searches and parses paths of python virtual enviroments * from predefined lookout locations */ void AutoParsePythonPaths(); /** * @brief Check if pretrained model sub folder inside RESULTS FOLDER exist. */ bool IsModelExists(const QString &, const QString &, const QString &); /** * @brief Clears all combo boxes * Any new combo box added in the future can be featured here for clearance. * */ void ClearAllComboBoxes(); /** * @brief Disable/deactivates the nnUNet GUI. * Clears any multi modal labels and selection widgets, as well. */ void DisableEverything(); /** * @brief Checks if nnUNet_predict command is valid in the selected python virtual environment. * * @return bool */ bool IsNNUNetInstalled(const QString &); /** * @brief Mapper function to map QString entries from UI to ModelParam attributes. * * @return mitk::ModelParams */ mitk::ModelParams MapToRequest( const QString &, const QString &, const QString &, const QString &, const std::vector &); /** * @brief Returns checked fold names from the ctk-Checkable-ComboBox. * * @return std::vector */ std::vector FetchSelectedFoldsFromUI(ctkCheckableComboBox *); /** * @brief Returns all paths from the dynamically generated ctk-path-line-edit boxes. * * @return std::vector */ std::vector FetchMultiModalImagesFromUI(); /** * @brief Updates cache count on UI. * */ void UpdateCacheCountOnUI(); Ui_QmitknnUNetToolGUIControls m_Controls; QmitkGPULoader m_GpuLoader; /** * @brief Stores all dynamically added ctk-path-line-edit UI elements. * */ std::vector m_Modalities; std::vector m_ModalLabels; std::vector> m_EnsembleParams; mitk::NodePredicateBase::Pointer m_MultiModalPredicate; QString m_PythonPath; /** * @brief Stores row count of the "advancedSettingsLayout" layout element. This value helps dynamically add * ctk-path-line-edit UI elements at the right place. Forced to initialize in the InitializeUI method since there is * no guarantee of retrieving exact row count anywhere else. * */ int m_UI_ROWS; /** * @brief Stores path of the model director (RESULTS_FOLDER appended by "nnUNet"). * */ std::shared_ptr m_ParentFolder = nullptr; /** * @brief Valid list of models supported by nnUNet * */ const QStringList m_VALID_MODELS = {"2d", "3d_lowres", "3d_fullres", "3d_cascade_fullres", "ensembles"}; const QString m_CACHE_COUNT_BASE_LABEL = "Cached Items: "; /** * @brief For storing values across sessions. Currently, RESULTS_FOLDER value is cached using this. */ QSettings m_Settings; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp index 4b82f39ed2..1b1308af45 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp @@ -1,478 +1,478 @@ #include "QmitknnUNetToolGUI.h" #include #include #include #include #include void QmitknnUNetToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); } void QmitknnUNetToolGUI::ClearAllModalities() { m_Controls.multiModalSpinBox->setMinimum(0); m_Controls.multiModalBox->setChecked(false); ClearAllModalLabels(); } void QmitknnUNetToolGUI::ClearAllModalLabels() { for (auto modalLabel : m_ModalLabels) { delete modalLabel; // delete the layout item m_ModalLabels.pop_back(); } m_Controls.advancedSettingsLayout->update(); } void QmitknnUNetToolGUI::DisableEverything() { m_Controls.modeldirectoryBox->setEnabled(false); m_Controls.refreshdirectoryBox->setEnabled(false); m_Controls.previewButton->setEnabled(false); m_Controls.multiModalSpinBox->setVisible(false); m_Controls.multiModalBox->setEnabled(false); ClearAllComboBoxes(); ClearAllModalities(); } void QmitknnUNetToolGUI::ClearAllComboBoxes() { m_Controls.modelBox->clear(); m_Controls.taskBox->clear(); m_Controls.foldBox->clear(); m_Controls.trainerBox->clear(); m_Controls.plannerBox->clear(); for (auto &layout : m_EnsembleParams) { layout->modelBox->clear(); layout->trainerBox->clear(); layout->plannerBox->clear(); layout->foldBox->clear(); } } void QmitknnUNetToolGUI::OnRefreshPresssed() { const QString resultsFolder = m_Controls.modeldirectoryBox->directory(); OnDirectoryChanged(resultsFolder); } void QmitknnUNetToolGUI::OnDirectoryChanged(const QString &resultsFolder) { m_Controls.previewButton->setEnabled(false); ClearAllComboBoxes(); ClearAllModalities(); m_ParentFolder = std::make_shared(resultsFolder); auto tasks = m_ParentFolder->getAllTasks(); tasks.removeDuplicates(); std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); }); m_Settings.setValue("nnUNet/LastRESULTS_FOLDERPath", resultsFolder); } void QmitknnUNetToolGUI::OnModelChanged(const QString &model) { if (model.isEmpty()) { return; } ClearAllModalities(); auto selectedTask = m_Controls.taskBox->currentText(); ctkComboBox *box = qobject_cast(sender()); if (box == m_Controls.modelBox) { if (model == m_VALID_MODELS.last()) { m_Controls.trainerBox->setVisible(false); m_Controls.trainerLabel->setVisible(false); m_Controls.plannerBox->setVisible(false); m_Controls.plannerLabel->setVisible(false); m_Controls.foldBox->setVisible(false); m_Controls.foldLabel->setVisible(false); ShowEnsembleLayout(true); auto models = m_ParentFolder->getModelsForTask(m_Controls.taskBox->currentText()); models.removeDuplicates(); models.removeOne(m_VALID_MODELS.last()); for (auto &layout : m_EnsembleParams) { layout->modelBox->clear(); layout->trainerBox->clear(); layout->plannerBox->clear(); std::for_each(models.begin(), models.end(), [&layout, this](QString model) { if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) layout->modelBox->addItem(model); }); } m_Controls.previewButton->setEnabled(true); } else { m_Controls.trainerBox->setVisible(true); m_Controls.trainerLabel->setVisible(true); m_Controls.plannerBox->setVisible(true); m_Controls.plannerLabel->setVisible(true); m_Controls.foldBox->setVisible(true); m_Controls.foldLabel->setVisible(true); m_Controls.previewButton->setEnabled(false); ShowEnsembleLayout(false); auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask(selectedTask, model); QStringList trainers, planners; std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); m_Controls.trainerBox->clear(); m_Controls.plannerBox->clear(); std::for_each( trainers.begin(), trainers.end(), [this](QString trainer) { m_Controls.trainerBox->addItem(trainer); }); std::for_each( planners.begin(), planners.end(), [this](QString planner) { m_Controls.plannerBox->addItem(planner); }); } } else if (!m_EnsembleParams.empty()) { for (auto &layout : m_EnsembleParams) { if (box == layout->modelBox) { layout->trainerBox->clear(); layout->plannerBox->clear(); auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask(selectedTask, model); QStringList trainers, planners; std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); std::for_each(trainers.begin(), trainers.end(), [&layout](const QString &trainer) { layout->trainerBox->addItem(trainer); }); std::for_each(planners.begin(), planners.end(), [&layout](const QString &planner) { layout->plannerBox->addItem(planner); }); break; } } } } void QmitknnUNetToolGUI::OnTaskChanged(const QString &task) { if (task.isEmpty()) { return; } m_Controls.modelBox->clear(); auto models = m_ParentFolder->getModelsForTask(task); models.removeDuplicates(); if (!models.contains(m_VALID_MODELS.last(), Qt::CaseInsensitive)) { models << m_VALID_MODELS.last(); // add ensemble even if folder doesn't exist } std::for_each(models.begin(), models.end(), [this](QString model) { if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) m_Controls.modelBox->addItem(model); }); } void QmitknnUNetToolGUI::OnTrainerChanged(const QString &plannerSelected) { if (plannerSelected.isEmpty()) { return; } auto *box = qobject_cast(sender()); if (box == m_Controls.plannerBox) { m_Controls.foldBox->clear(); auto selectedTrainer = m_Controls.trainerBox->currentText(); auto selectedTask = m_Controls.taskBox->currentText(); auto selectedModel = m_Controls.modelBox->currentText(); auto folds = m_ParentFolder->getFoldsForTrainerPlanner( selectedTrainer, plannerSelected, selectedTask, selectedModel); std::for_each(folds.begin(), folds.end(), [this](QString fold) { if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet m_Controls.foldBox->addItem(fold); }); if (m_Controls.foldBox->count() != 0) { CheckAllInCheckableComboBox(m_Controls.foldBox); m_Controls.previewButton->setEnabled(true); const QString parentPath = QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + selectedModel + QDir::separator() + selectedTask + QDir::separator() + selectedTrainer + QString("__") + plannerSelected); const QString jsonPath = this->DumpJSONfromPickle(parentPath); if (!jsonPath.isEmpty()) { this->DisplayMultiModalInfoFromJSON(jsonPath); } } } else if (!m_EnsembleParams.empty()) { for (auto &layout : m_EnsembleParams) { if (box == layout->plannerBox) { layout->foldBox->clear(); auto selectedTrainer = layout->trainerBox->currentText(); auto selectedTask = m_Controls.taskBox->currentText(); auto selectedModel = layout->modelBox->currentText(); auto folds = m_ParentFolder->getFoldsForTrainerPlanner( selectedTrainer, plannerSelected, selectedTask, selectedModel); std::for_each(folds.begin(), folds.end(), [&layout](const QString &fold) { if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet layout->foldBox->addItem(fold); }); if (layout->foldBox->count() != 0) { CheckAllInCheckableComboBox(layout->foldBox); m_Controls.previewButton->setEnabled(true); const QString parentPath = QDir::cleanPath( m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + selectedModel + QDir::separator() + selectedTask + QDir::separator() + selectedTrainer + QString("__") + plannerSelected); const QString jsonPath = this->DumpJSONfromPickle(parentPath); if (!jsonPath.isEmpty()) { this->DisplayMultiModalInfoFromJSON(jsonPath); } } break; } } } } void QmitknnUNetToolGUI::OnPythonPathChanged(const QString &pyEnv) { if (pyEnv == QString("Select")) { QString path = QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir"); if (!path.isEmpty()) { OnPythonPathChanged(path); // recall same function for new path validation m_Controls.pythonEnvComboBox->insertItem(0, path); m_Controls.pythonEnvComboBox->setCurrentIndex(0); } } else if (!IsNNUNetInstalled(pyEnv)) { std::string warning = "WARNING: nnUNet is not detected on the Python environment you selected. Please select another " "environment or create one. For more info refer https://github.com/MIC-DKFZ/nnUNet"; ShowErrorMessage(warning); DisableEverything(); } else { m_Controls.modeldirectoryBox->setEnabled(true); m_Controls.previewButton->setEnabled(true); m_Controls.refreshdirectoryBox->setEnabled(true); m_Controls.multiModalBox->setEnabled(true); QString setVal = FetchResultsFolderFromEnv(); if (!setVal.isEmpty()) { m_Controls.modeldirectoryBox->setDirectory(setVal); } OnRefreshPresssed(); m_PythonPath = pyEnv.mid(pyEnv.indexOf(" ") + 1); if (!(m_PythonPath.endsWith("bin", Qt::CaseInsensitive) || m_PythonPath.endsWith("bin/", Qt::CaseInsensitive))) { m_PythonPath += QDir::separator() + QString("bin"); } } } void QmitknnUNetToolGUI::OnCheckBoxChanged(int state) { bool visibility = false; if (state == Qt::Checked) { visibility = true; } ctkCheckBox *box = qobject_cast(sender()); if (box != nullptr) { if (box->objectName() == QString("multiModalBox")) { m_Controls.multiModalSpinLabel->setVisible(visibility); m_Controls.multiModalSpinBox->setVisible(visibility); if (visibility) { QmitkDataStorageComboBox *defaultImage = new QmitkDataStorageComboBox(this, true); defaultImage->setObjectName(QString("multiModal_" + QString::number(0))); defaultImage->SetPredicate(m_MultiModalPredicate); mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); if (tool != nullptr) { defaultImage->SetDataStorage(tool->GetDataStorage()); defaultImage->SetSelectedNode(tool->GetRefNode()); } m_Controls.advancedSettingsLayout->addWidget(defaultImage, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); m_Modalities.push_back(defaultImage); } else { OnModalitiesNumberChanged(0); m_Controls.multiModalSpinBox->setValue(0); delete m_Modalities[0]; m_Modalities.pop_back(); ClearAllModalLabels(); } } } } void QmitknnUNetToolGUI::OnModalitiesNumberChanged(int num) { while (num > static_cast(m_Modalities.size() - 1)) { QmitkDataStorageComboBox *multiModalBox = new QmitkDataStorageComboBox(this, true); mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); multiModalBox->SetDataStorage(tool->GetDataStorage()); multiModalBox->SetPredicate(m_MultiModalPredicate); multiModalBox->setObjectName(QString("multiModal_" + QString::number(m_Modalities.size() + 1))); m_Controls.advancedSettingsLayout->addWidget(multiModalBox, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); m_Modalities.push_back(multiModalBox); } while (num < static_cast(m_Modalities.size() - 1) && !m_Modalities.empty()) { QmitkDataStorageComboBox *child = m_Modalities.back(); if (child->objectName() == "multiModal_0") { std::iter_swap(m_Modalities.end() - 2, m_Modalities.end() - 1); child = m_Modalities.back(); } delete child; // delete the layout item m_Modalities.pop_back(); } m_Controls.advancedSettingsLayout->update(); } void QmitknnUNetToolGUI::AutoParsePythonPaths() { QString homeDir = QDir::homePath(); std::vector searchDirs; #ifdef _WIN32 searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() + QString("anaconda3")); #else // Add search locations for possible standard python paths here searchDirs.push_back(homeDir + QDir::separator() + "environments"); searchDirs.push_back(homeDir + QDir::separator() + "anaconda3"); searchDirs.push_back(homeDir + QDir::separator() + "miniconda3"); searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3"); searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3"); #endif for (QString searchDir : searchDirs) { if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive)) { if (QDir(searchDir).exists()) { m_Controls.pythonEnvComboBox->insertItem(0, "(base): " + searchDir); searchDir.append((QDir::separator() + QString("envs"))); } } for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();) { subIt.next(); QString envName = subIt.fileName(); if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any. { m_Controls.pythonEnvComboBox->insertItem(0, "(" + envName + "): " + subIt.filePath()); } } } m_Controls.pythonEnvComboBox->setCurrentIndex(-1); } mitk::ModelParams QmitknnUNetToolGUI::MapToRequest(const QString &modelName, const QString &taskName, const QString &trainer, const QString &planId, const std::vector &folds) { mitk::ModelParams requestObject; requestObject.model = modelName.toStdString(); requestObject.trainer = trainer.toStdString(); requestObject.planId = planId.toStdString(); requestObject.task = taskName.toStdString(); requestObject.folds = folds; mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); requestObject.inputName = tool->GetRefNode()->GetName(); requestObject.timeStamp = std::to_string(mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint()); return requestObject; } void QmitknnUNetToolGUI::SegmentationProcessFailed() { WriteErrorMessage( "STATUS: Error in the segmentation process.
No resulting segmentation can be loaded.
"); this->setCursor(Qt::ArrowCursor); std::stringstream stream; stream << "Error in the segmentation process. No resulting segmentation can be loaded."; ShowErrorMessage(stream.str()); } void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool, bool forceRender) { if (forceRender) { tool->RenderOutputBuffer(); } - this->SetLabelSetPreview(tool->GetMLPreview()); + this->SetLabelSetPreview(tool->GetPreviewSegmentation()); WriteStatusMessage("STATUS: Segmentation task finished successfully.
If multiple Preview objects are selected to Confirm, " "they will be merged. Any unselected Preview objects will be lost.
"); } void QmitknnUNetToolGUI::ShowEnsembleLayout(bool visible) { if (m_EnsembleParams.empty()) { ctkCollapsibleGroupBox *groupBoxModel1 = new ctkCollapsibleGroupBox(this); auto lay1 = std::make_unique(groupBoxModel1); groupBoxModel1->setObjectName(QString::fromUtf8("model_1_Box")); groupBoxModel1->setTitle(QString::fromUtf8("Model 1")); groupBoxModel1->setMinimumSize(QSize(0, 0)); groupBoxModel1->setCollapsedHeight(5); groupBoxModel1->setCollapsed(false); groupBoxModel1->setFlat(true); groupBoxModel1->setAlignment(Qt::AlignRight); m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 5, 0, 1, 2); connect(lay1->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect( lay1->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); m_EnsembleParams.push_back(std::move(lay1)); ctkCollapsibleGroupBox *groupBoxModel2 = new ctkCollapsibleGroupBox(this); auto lay2 = std::make_unique(groupBoxModel2); groupBoxModel2->setObjectName(QString::fromUtf8("model_2_Box")); groupBoxModel2->setTitle(QString::fromUtf8("Model 2")); groupBoxModel2->setMinimumSize(QSize(0, 0)); groupBoxModel2->setCollapsedHeight(5); groupBoxModel2->setCollapsed(false); groupBoxModel2->setFlat(true); groupBoxModel2->setAlignment(Qt::AlignLeft); m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 5, 2, 1, 2); connect(lay2->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect( lay2->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); m_EnsembleParams.push_back(std::move(lay2)); } for (auto &layout : m_EnsembleParams) { layout->setVisible(visible); } } diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index 54b348aa7c..26dbc03a4f 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,79 +1,76 @@ set( CPP_FILES -Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp -Qmitk/QmitkAutoSegmentationToolGUIBase.cpp -Qmitk/QmitkAutoMLSegmentationToolGUIBase.cpp +Qmitk/QmitkSegWithPreviewToolGUIBase.cpp +Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUI.cpp Qmitk/QmitkBinaryThresholdULToolGUI.cpp Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp Qmitk/QmitkConfirmSegmentationDialog.cpp Qmitk/QmitkCopyToClipBoardDialog.cpp Qmitk/QmitkDrawPaintbrushToolGUI.cpp Qmitk/QmitkErasePaintbrushToolGUI.cpp Qmitk/QmitkLiveWireTool2DGUI.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/QmitknnUNetToolGUI.cpp Qmitk/QmitknnUNetToolSlots.cpp Qmitk/QmitkSurfaceStampWidget.cpp Qmitk/QmitkMaskStampWidget.cpp Qmitk/QmitkSliceBasedInterpolatorWidget.cpp Qmitk/QmitkStaticDynamicSegmentationDialog.cpp Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp Qmitk/QmitkSimpleLabelSetListWidget.cpp ) set(MOC_H_FILES -Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h -Qmitk/QmitkAutoSegmentationToolGUIBase.h -Qmitk/QmitkAutoMLSegmentationToolGUIBase.h +Qmitk/QmitkSegWithPreviewToolGUIBase.h +Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUI.h Qmitk/QmitkBinaryThresholdULToolGUI.h Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h Qmitk/QmitkConfirmSegmentationDialog.h Qmitk/QmitkCopyToClipBoardDialog.h Qmitk/QmitkDrawPaintbrushToolGUI.h Qmitk/QmitkErasePaintbrushToolGUI.h Qmitk/QmitkLiveWireTool2DGUI.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/QmitknnUNetToolGUI.h Qmitk/QmitknnUNetGPU.h Qmitk/QmitknnUNetEnsembleLayout.h Qmitk/QmitknnUNetFolderParser.h Qmitk/QmitkSurfaceStampWidget.h Qmitk/QmitkMaskStampWidget.h Qmitk/QmitkSliceBasedInterpolatorWidget.h Qmitk/QmitkStaticDynamicSegmentationDialog.h Qmitk/QmitkSurfaceBasedInterpolatorWidget.h Qmitk/QmitkSimpleLabelSetListWidget.h ) set(UI_FILES -Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui Qmitk/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkOtsuToolWidgetControls.ui Qmitk/QmitkLiveWireTool2DGUIControls.ui Qmitk/QmitkSurfaceStampWidgetGUIControls.ui Qmitk/QmitkMaskStampWidgetGUIControls.ui Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitknnUNetToolGUIControls.ui ) set(QRC_FILES resources/SegmentationUI.qrc )