diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp index 15fab21d0f..3b2c41f166 100644 --- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp +++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp @@ -1,860 +1,861 @@ /*============================================================================ 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 <mitkSurfaceInterpolationController.h> #include <shared_mutex> #include <mitkCreateDistanceImageFromSurfaceFilter.h> #include <mitkComputeContourSetNormalsFilter.h> #include <mitkImageAccessByItk.h> #include <mitkImagePixelReadAccessor.h> #include <mitkImageTimeSelector.h> #include <mitkImageToSurfaceFilter.h> #include <mitkLabelSetImage.h> #include <mitkMemoryUtilities.h> #include <mitkNodePredicateDataUID.h> #include <mitkNodePredicateProperty.h> #include <mitkNodePredicateAnd.h> #include <mitkPlanarCircle.h> #include <mitkPlaneGeometry.h> #include <mitkReduceContourSetFilter.h> #include <vtkFieldData.h> #include <vtkMath.h> #include <vtkPolygon.h> struct CPICache { mitk::SurfaceInterpolationController::CPIVector cpis; itk::TimeStamp cpiTimeStamp; mitk::Surface::Pointer cachedSurface; }; typedef std::map<mitk::TimeStepType, CPICache> CPITimeStepMap; typedef std::map<mitk::LabelSetImage::LabelValueType, CPITimeStepMap> CPITimeStepLabelMap; typedef std::map<const mitk::LabelSetImage*, CPITimeStepLabelMap> CPITimeStepLabelSegMap; CPITimeStepLabelSegMap cpiMap; std::shared_mutex cpiMutex; std::map<mitk::LabelSetImage*, unsigned long> segmentationObserverTags; mitk::SurfaceInterpolationController::SurfaceInterpolationController() : m_DistanceImageVolume(50000), m_SelectedSegmentation(nullptr) { } mitk::SurfaceInterpolationController::~SurfaceInterpolationController() { this->RemoveObservers(); } void mitk::SurfaceInterpolationController::RemoveObservers() { // Removing all observers while (segmentationObserverTags.size()) { this->RemoveObserversInternal(segmentationObserverTags.begin()->first); } } mitk::SurfaceInterpolationController *mitk::SurfaceInterpolationController::GetInstance() { static mitk::SurfaceInterpolationController::Pointer m_Instance; if (m_Instance.IsNull()) { m_Instance = SurfaceInterpolationController::New(); } return m_Instance; } void mitk::SurfaceInterpolationController::AddNewContours(const std::vector<ContourPositionInformation>& newCPIs, bool reinitializationAction, bool silent) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) return; for (auto cpi : newCPIs) { if (cpi.Contour->GetVtkPolyData()->GetNumberOfPoints() > 0) { this->AddToCPIMap(cpi, reinitializationAction); } } if (!silent) this->Modified(); } mitk::DataNode* GetSegmentationImageNodeInternal(mitk::DataStorage* ds, const mitk::LabelSetImage* seg) { if (nullptr == ds) return nullptr; if (nullptr == seg) return nullptr; mitk::DataNode* segmentationNode = nullptr; mitk::NodePredicateDataUID::Pointer dataUIDPredicate = mitk::NodePredicateDataUID::New(seg->GetUID()); auto dataNodeObjects = ds->GetSubset(dataUIDPredicate); if (dataNodeObjects->Size() != 0) { for (auto it = dataNodeObjects->Begin(); it != dataNodeObjects->End(); ++it) { segmentationNode = it->Value(); } } else { MITK_ERROR << "Unable to find the labelSetImage with the desired UID."; } return segmentationNode; } mitk::DataNode* mitk::SurfaceInterpolationController::GetSegmentationImageNode() const { if (m_DataStorage.IsNull()) return nullptr; auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) return nullptr; return GetSegmentationImageNodeInternal(this->m_DataStorage, selectedSegmentation); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::SurfaceInterpolationController::GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) const { DataStorage::SetOfObjects::Pointer relevantNodes = DataStorage::SetOfObjects::New(); if (m_DataStorage.IsNotNull()) { //remove relevant plane nodes auto nodes = this->GetPlaneGeometryNodeFromDataStorage(segNode, labelValue); for (auto it = nodes->Begin(); it != nodes->End(); ++it) { auto aTS = dynamic_cast<mitk::IntProperty*>(it->Value()->GetProperty("timeStep"))->GetValue(); bool sameTS = (timeStep == aTS); if (sameTS) { relevantNodes->push_back(it->Value()); } } } return relevantNodes; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::SurfaceInterpolationController::GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue) const { auto isContourPlaneGeometry = NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true)); auto isCorrectLabel = NodePredicateProperty::New("labelID", mitk::UShortProperty::New(labelValue)); auto searchPredicate = NodePredicateAnd::New(isContourPlaneGeometry, isCorrectLabel); mitk::DataStorage::SetOfObjects::ConstPointer result; if (m_DataStorage.IsNotNull()) result = m_DataStorage->GetDerivations(segNode, searchPredicate); return result; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::SurfaceInterpolationController::GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode) const { auto isContourPlaneGeometry = NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer result; if (m_DataStorage.IsNotNull()) result = m_DataStorage->GetDerivations(segNode, isContourPlaneGeometry); return result; } void mitk::SurfaceInterpolationController::AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo) const { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) { mitkThrow()<< "Cannot add plane geometries. No valid segmentation selected."; } if (!selectedSegmentation->GetTimeGeometry()->IsValidTimeStep(contourInfo.TimeStep)) { MITK_ERROR << "Invalid time point requested in AddPlaneGeometryNodeToDataStorage."; return; } if (m_DataStorage.IsNull()) { MITK_DEBUG << "Cannot add plane geometry nodes. No data storage is set."; return; } auto planeGeometry = contourInfo.Plane; if (planeGeometry) { auto segmentationNode = this->GetSegmentationImageNode(); mitk::DataStorage::SetOfObjects::ConstPointer contourNodes = this->GetPlaneGeometryNodeFromDataStorage(segmentationNode, contourInfo.LabelValue, contourInfo.TimeStep); mitk::DataNode::Pointer contourPlaneGeometryDataNode; // Go through the pre-existing contours and check if the contour position matches them. for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it) { auto planeData = dynamic_cast<mitk::PlanarFigure*>(it->Value()->GetData()); if (nullptr == planeData) mitkThrow() << "Invalid ContourPlaneGeometry data node. Does not contion a planar figure as data."; bool samePlane = contourInfo.Plane->IsOnPlane(planeData->GetPlaneGeometry()); if (samePlane) { contourPlaneGeometryDataNode = it->Value(); break; } } // Go through the contourPlaneGeometry Data and add the segmentationNode to it. if (contourPlaneGeometryDataNode.IsNull()) { auto planeGeometryData = mitk::PlanarCircle::New(); planeGeometryData->SetPlaneGeometry(planeGeometry->Clone()); mitk::Point2D p1; planeGeometry->Map(planeGeometry->GetCenter(), p1); planeGeometryData->PlaceFigure(p1); planeGeometryData->SetCurrentControlPoint(p1); planeGeometryData->SetProperty("initiallyplaced", mitk::BoolProperty::New(true)); std::string contourName = "contourPlane L " + std::to_string(contourInfo.LabelValue) + " T " + std::to_string(contourInfo.TimeStep); contourPlaneGeometryDataNode = mitk::DataNode::New(); contourPlaneGeometryDataNode->SetData(planeGeometryData); // No need to change properties contourPlaneGeometryDataNode->SetProperty("helper object", mitk::BoolProperty::New(false)); contourPlaneGeometryDataNode->SetProperty("hidden object", mitk::BoolProperty::New(true)); contourPlaneGeometryDataNode->SetProperty("isContourPlaneGeometry", mitk::BoolProperty::New(true)); contourPlaneGeometryDataNode->SetVisibility(false); // Need to change properties contourPlaneGeometryDataNode->SetProperty("name", mitk::StringProperty::New(contourName) ); contourPlaneGeometryDataNode->SetProperty("labelID", mitk::UShortProperty::New(contourInfo.LabelValue)); contourPlaneGeometryDataNode->SetProperty("timeStep", mitk::IntProperty::New(contourInfo.TimeStep)); contourPlaneGeometryDataNode->SetData(planeGeometryData); m_DataStorage->Add(contourPlaneGeometryDataNode, segmentationNode); } } } void mitk::SurfaceInterpolationController::AddToCPIMap(ContourPositionInformation& contourInfo, bool reinitializationAction) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) return; if (!selectedSegmentation->GetTimeGeometry()->IsValidTimeStep(contourInfo.TimeStep)) { MITK_ERROR << "Invalid time step requested for interpolation pipeline."; return; } if (contourInfo.Plane == nullptr) { MITK_ERROR << "contourInfo plane is null."; return; } if (contourInfo.Contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { this->RemoveContour(contourInfo); MITK_DEBUG << "contourInfo contour is empty."; return; } { std::lock_guard<std::shared_mutex> guard(cpiMutex); const auto& currentTimeStep = contourInfo.TimeStep; const auto& currentLabelValue = contourInfo.LabelValue; auto& currentImageContours = cpiMap[selectedSegmentation]; auto& currentLabelContours = currentImageContours[currentLabelValue]; auto& currentCPICache = currentLabelContours[currentTimeStep]; auto& currentContourList = currentCPICache.cpis; auto finding = std::find_if(currentContourList.begin(), currentContourList.end(), [contourInfo](const ContourPositionInformation& element) {return contourInfo.Plane->IsOnPlane(element.Plane); }); if (finding != currentContourList.end()) { MITK_DEBUG << "CPI already exists. CPI is updated. Label: "<< currentLabelValue << "; Time Step: " << currentTimeStep; *finding = contourInfo; } else { currentContourList.push_back(contourInfo); } currentCPICache.cpiTimeStamp.Modified(); } if (!reinitializationAction) { this->AddPlaneGeometryNodeToDataStorage(contourInfo); } } bool mitk::SurfaceInterpolationController::RemoveContour(ContourPositionInformation contourInfo, bool keepPlaceholderForUndo) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) { return false; } if (!selectedSegmentation->GetTimeGeometry()->IsValidTimeStep(contourInfo.TimeStep)) { return false; } bool removedIt = false; { std::lock_guard<std::shared_mutex> cpiGuard(cpiMutex); const auto currentTimeStep = contourInfo.TimeStep; const auto currentLabel = contourInfo.LabelValue; auto& cpiCache = cpiMap.at(selectedSegmentation).at(currentLabel).at(currentTimeStep); auto it = cpiCache.cpis.begin(); while (it != cpiCache.cpis.end()) { const ContourPositionInformation& currentContour = (*it); if (currentContour.Plane->IsOnPlane(contourInfo.Plane)) { if (keepPlaceholderForUndo) { it->Contour = nullptr; } else { cpiCache.cpis.erase(it); } cpiCache.cpiTimeStamp.Modified(); removedIt = true; if (m_DataStorage.IsNotNull()) { mitk::DataNode::Pointer contourPlaneGeometryDataNode; auto contourNodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(m_DataStorage, selectedSegmentation), currentLabel, currentTimeStep); // Go through the nodes and check if the contour position matches them. for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it) { auto planeData = dynamic_cast<mitk::PlanarFigure*>(it->Value()->GetData()); if (nullptr == planeData) mitkThrow() << "Invalid ContourPlaneGeometry data node. Does not contion a planar figure as data."; bool samePlane = contourInfo.Plane->IsOnPlane(planeData->GetPlaneGeometry()); if (samePlane) { m_DataStorage->Remove(it->Value()); break; } } } break; } ++it; } } return removedIt; } void mitk::SurfaceInterpolationController::AddActiveLabelContoursForInterpolation(ReduceContourSetFilter* reduceFilter, const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) { - std::shared_lock<std::shared_mutex> guard(cpiMutex); - const auto& currentImageContours = cpiMap.at(segmentationImage); auto finding = currentImageContours.find(labelValue); if (finding == currentImageContours.end()) { MITK_INFO << "Contours for label don't exist. Label value: " << labelValue; return; } const auto& currentLabelContoursMap = finding->second; auto tsfinding = currentLabelContoursMap.find(timeStep); if (tsfinding == currentLabelContoursMap.end()) { MITK_INFO << "Contours for current time step don't exist."; return; } const auto& currentContours = tsfinding->second.cpis; unsigned int index = 0; for (const auto& cpi : currentContours) { if (!cpi.IsPlaceHolder()) { reduceFilter->SetInput(index, cpi.Contour); ++index; } } } bool CPICacheIsOutdated(const mitk::LabelSetImage* segmentationImage, mitk::LabelSetImage::LabelValueType labelValue, mitk::TimeStepType timeStep) { const auto& currentImageContours = cpiMap.at(segmentationImage); auto finding = currentImageContours.find(labelValue); if (finding == currentImageContours.end()) { return false; } const auto& currentLabelContoursMap = finding->second; auto tsfinding = currentLabelContoursMap.find(timeStep); if (tsfinding == currentLabelContoursMap.end()) { return false; } bool result = tsfinding->second.cachedSurface.IsNull() || tsfinding->second.cachedSurface->GetMTime() < tsfinding->second.cpiTimeStamp.GetMTime(); return result; } void SetCPICacheSurface(mitk::Surface* surface, const mitk::LabelSetImage* segmentationImage, mitk::LabelSetImage::LabelValueType labelValue, mitk::TimeStepType timeStep) { const auto& currentImageContours = cpiMap.at(segmentationImage); auto finding = currentImageContours.find(labelValue); if (finding == currentImageContours.end()) { return; } const auto& currentLabelContoursMap = finding->second; auto tsfinding = currentLabelContoursMap.find(timeStep); if (tsfinding == currentLabelContoursMap.end()) { return; } cpiMap[segmentationImage][labelValue][timeStep].cachedSurface = surface; } void mitk::SurfaceInterpolationController::Interpolate(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) { if (nullptr == segmentationImage) { mitkThrow() << "Cannot interpolate contours. No valid segmentation passed."; } + std::lock_guard<std::shared_mutex> guard(cpiMutex); auto it = cpiMap.find(segmentationImage); if (it == cpiMap.end()) { mitkThrow() << "Cannot interpolate contours. Passed segmentation is not registered at controller."; } if (!segmentationImage->ExistLabel(labelValue)) { mitkThrow() << "Cannot interpolate contours. None existant label request. Invalid label:" << labelValue; } if (!segmentationImage->GetTimeGeometry()->IsValidTimeStep(timeStep)) { mitkThrow() << "Cannot interpolate contours. No valid time step requested. Invalid time step:" << timeStep; } if (!CPICacheIsOutdated(segmentationImage, labelValue, timeStep)) return; mitk::Surface::Pointer interpolationResult = nullptr; auto reduceFilter = ReduceContourSetFilter::New(); auto normalsFilter = ComputeContourSetNormalsFilter::New(); auto interpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); const auto spacing = segmentationImage->GetGeometry(timeStep)->GetSpacing(); double minSpacing = 100; double maxSpacing = 0; for (int i = 0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } reduceFilter->SetMinSpacing(minSpacing); reduceFilter->SetMaxSpacing(maxSpacing); normalsFilter->SetMaxSpacing(maxSpacing); interpolateSurfaceFilter->SetDistanceImageVolume(m_DistanceImageVolume); reduceFilter->SetUseProgressBar(false); normalsFilter->SetUseProgressBar(true); normalsFilter->SetProgressStepSize(1); interpolateSurfaceFilter->SetUseProgressBar(true); interpolateSurfaceFilter->SetProgressStepSize(7); // Set reference image for interpolation surface filter itk::ImageBase<3>::Pointer itkImage = itk::ImageBase<3>::New(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(segmentationImage); timeSelector->SetTimeNr(timeStep); timeSelector->SetChannelNr(0); timeSelector->Update(); mitk::Image::Pointer refSegImage = timeSelector->GetOutput(); AccessFixedDimensionByItk_1(refSegImage, GetImageBase, 3, itkImage); interpolateSurfaceFilter->SetReferenceImage(itkImage.GetPointer()); try { this->AddActiveLabelContoursForInterpolation(reduceFilter, segmentationImage, labelValue, timeStep); reduceFilter->Update(); auto currentNumberOfReducedContours = reduceFilter->GetNumberOfOutputs(); if (currentNumberOfReducedContours < 2) { // If no interpolation is possible reset the interpolation result MITK_INFO << "Interpolation impossible: not enough contours."; } else { normalsFilter->SetSegmentationBinaryImage(refSegImage); for (size_t i = 0; i < currentNumberOfReducedContours; ++i) { mitk::Surface::Pointer reducedContour = reduceFilter->GetOutput(i); reducedContour->DisconnectPipeline(); normalsFilter->SetInput(i, reducedContour); interpolateSurfaceFilter->SetInput(i, normalsFilter->GetOutput(i)); } // Setting up progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo(10); // create a surface from the distance-image auto imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); imageToSurfaceFilter->SetInput(interpolateSurfaceFilter->GetOutput()); imageToSurfaceFilter->SetThreshold(0); imageToSurfaceFilter->SetSmooth(true); imageToSurfaceFilter->SetSmoothIteration(1); imageToSurfaceFilter->Update(); interpolationResult = mitk::Surface::New(); interpolationResult->Expand(segmentationImage->GetTimeSteps()); auto geometry = segmentationImage->GetTimeGeometry()->Clone(); geometry->ReplaceTimeStepGeometries(mitk::Geometry3D::New()); interpolationResult->SetTimeGeometry(geometry); interpolationResult->SetVtkPolyData(imageToSurfaceFilter->GetOutput()->GetVtkPolyData(), timeStep); interpolationResult->DisconnectPipeline(); // Last progress step mitk::ProgressBar::GetInstance()->Progress(20); } } catch (const Exception& e) { MITK_ERROR << "Interpolation failed: " << e.what(); interpolationResult = nullptr; } SetCPICacheSurface(interpolationResult, segmentationImage, labelValue, timeStep); } mitk::Surface::Pointer mitk::SurfaceInterpolationController::GetInterpolationResult(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) { if (nullptr == segmentationImage) { mitkThrow() << "Cannot interpolate contours. No valid segmentation passed."; } + std::shared_lock<std::shared_mutex> guard(cpiMutex); + if (cpiMap.find(segmentationImage) == cpiMap.end()) { mitkThrow() << "Cannot interpolate contours. Passed segmentation is not registered at controller."; } if (!segmentationImage->ExistLabel(labelValue)) { mitkThrow() << "Cannot interpolate contours. None existant label request. Invalid label:" << labelValue; } if (!segmentationImage->GetTimeGeometry()->IsValidTimeStep(timeStep)) { mitkThrow() << "Cannot interpolate contours. No valid time step requested. Invalid time step:" << timeStep; } const auto& currentImageContours = cpiMap.at(segmentationImage); auto finding = currentImageContours.find(labelValue); if (finding == currentImageContours.end()) { return nullptr; } const auto& currentLabelContoursMap = finding->second; auto tsfinding = currentLabelContoursMap.find(timeStep); if (tsfinding == currentLabelContoursMap.end()) { return nullptr; } return tsfinding->second.cachedSurface; } void mitk::SurfaceInterpolationController::SetDataStorage(DataStorage::Pointer ds) { m_DataStorage = ds; } void mitk::SurfaceInterpolationController::SetDistanceImageVolume(unsigned int distImgVolume) { m_DistanceImageVolume = distImgVolume; } mitk::LabelSetImage* mitk::SurfaceInterpolationController::GetCurrentSegmentation() { return m_SelectedSegmentation.Lock(); } unsigned int mitk::SurfaceInterpolationController::GetNumberOfInterpolationSessions() { return cpiMap.size(); } template <typename TPixel, unsigned int VImageDimension> void mitk::SurfaceInterpolationController::GetImageBase(itk::Image<TPixel, VImageDimension> *input, itk::ImageBase<3>::Pointer &result) { result->Graft(input); } void mitk::SurfaceInterpolationController::SetCurrentInterpolationSession(mitk::LabelSetImage* currentSegmentationImage) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (currentSegmentationImage == selectedSegmentation) { return; } m_SelectedSegmentation = currentSegmentationImage; selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNotNull()) { std::lock_guard<std::shared_mutex> guard(cpiMutex); auto it = cpiMap.find(selectedSegmentation); if (it == cpiMap.end()) { cpiMap[selectedSegmentation] = CPITimeStepLabelMap(); auto command = itk::MemberCommand<SurfaceInterpolationController>::New(); command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted); segmentationObserverTags[selectedSegmentation] = selectedSegmentation->AddObserver(itk::DeleteEvent(), command); selectedSegmentation->AddLabelRemovedListener(mitk::MessageDelegate1<SurfaceInterpolationController, mitk::LabelSetImage::LabelValueType>( this, &SurfaceInterpolationController::OnRemoveLabel)); } } } void mitk::SurfaceInterpolationController::RemoveInterpolationSession(const mitk::LabelSetImage* segmentationImage) { if (nullptr != segmentationImage) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation == segmentationImage) { this->SetCurrentInterpolationSession(nullptr); } { std::lock_guard<std::shared_mutex> guard(cpiMutex); this->RemoveObserversInternal(segmentationImage); cpiMap.erase(segmentationImage); if (m_DataStorage.IsNotNull()) { auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, segmentationImage)); this->m_DataStorage->Remove(nodes); } } } } void mitk::SurfaceInterpolationController::RemoveObserversInternal(const mitk::LabelSetImage* segmentationImage) { auto pos = segmentationObserverTags.find(const_cast<mitk::LabelSetImage*>(segmentationImage)); if (pos != segmentationObserverTags.end()) { pos->first->RemoveObserver((*pos).second); segmentationImage->RemoveLabelRemovedListener(mitk::MessageDelegate1<SurfaceInterpolationController, mitk::LabelSetImage::LabelValueType>( this, &SurfaceInterpolationController::OnRemoveLabel)); segmentationObserverTags.erase(const_cast<mitk::LabelSetImage*>(segmentationImage)); } } void mitk::SurfaceInterpolationController::RemoveAllInterpolationSessions() { while (!cpiMap.empty()) { this->RemoveInterpolationSession(cpiMap.begin()->first); } } void mitk::SurfaceInterpolationController::RemoveContours(mitk::Label::PixelType label, TimeStepType timeStep) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) { mitkThrow() << "Cannot remove contours. No valid segmentation selected."; } std::lock_guard<std::shared_mutex> guard(cpiMutex); auto& cpiLabelMap = cpiMap[selectedSegmentation]; auto finding = cpiLabelMap.find(label); if (finding != cpiLabelMap.end()) { finding->second.erase(timeStep); } if (m_DataStorage.IsNotNull()) { //remove relevant plane nodes auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, selectedSegmentation), label, timeStep); this->m_DataStorage->Remove(nodes); } this->Modified(); } void mitk::SurfaceInterpolationController::RemoveContours(mitk::Label::PixelType label) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNull()) { mitkThrow() << "Cannot remove contours. No valid segmentation selected."; } std::lock_guard<std::shared_mutex> guard(cpiMutex); cpiMap[selectedSegmentation].erase(label); if (m_DataStorage.IsNotNull()) { //remove relevant plane nodes auto nodes = this->GetPlaneGeometryNodeFromDataStorage(GetSegmentationImageNodeInternal(this->m_DataStorage, selectedSegmentation), label); this->m_DataStorage->Remove(nodes); } this->Modified(); } void mitk::SurfaceInterpolationController::OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject & /*event*/) { auto tempImage = dynamic_cast<mitk::LabelSetImage *>(const_cast<itk::Object *>(caller)); if (tempImage) { this->RemoveInterpolationSession(tempImage); } } void mitk::SurfaceInterpolationController::OnRemoveLabel(mitk::Label::PixelType removedLabelValue) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation.IsNotNull()) { this->RemoveContours(removedLabelValue); } } mitk::SurfaceInterpolationController::CPIVector* mitk::SurfaceInterpolationController::GetContours(LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation == nullptr) return nullptr; std::shared_lock<std::shared_mutex> guard(cpiMutex); auto labelFinding = cpiMap[selectedSegmentation].find(labelValue); if (labelFinding != cpiMap[selectedSegmentation].end()) { auto tsFinding = labelFinding->second.find(timeStep); if (tsFinding != labelFinding->second.end()) { return &(tsFinding->second.cpis); } } return nullptr; } std::vector<mitk::LabelSetImage::LabelValueType> mitk::SurfaceInterpolationController::GetAffectedLabels(const LabelSetImage* seg, TimeStepType timeStep, const PlaneGeometry* plane) const { std::lock_guard<std::shared_mutex> guard(cpiMutex); std::vector<mitk::LabelSetImage::LabelValueType> result; auto finding = cpiMap.find(seg); if (finding == cpiMap.end()) return result; const auto& currentImageContours = cpiMap[seg]; for (const auto& [label, contours] : currentImageContours) { auto tsFinding = contours.find(timeStep); if (tsFinding != contours.end()) { const auto& cpis = contours.at(timeStep).cpis; auto finding = std::find_if(cpis.begin(), cpis.end(), [plane](const ContourPositionInformation& element) {return plane->IsOnPlane(element.Plane); }); if (finding != cpis.end()) { result.push_back(label); } } } return result; } void mitk::SurfaceInterpolationController::CompleteReinitialization(const std::vector<ContourPositionInformation>& newCPIs) { this->ClearInterpolationSession(); // Now the layers should be empty and the new layers can be added. this->AddNewContours(newCPIs, true); } void mitk::SurfaceInterpolationController::ClearInterpolationSession() { auto selectedSegmentation = m_SelectedSegmentation.Lock(); if (selectedSegmentation != nullptr) { std::lock_guard<std::shared_mutex> guard(cpiMutex); cpiMap[selectedSegmentation].clear(); } }