diff --git a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp index 5d092288cd..1482b9ddcd 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp @@ -1,175 +1,242 @@ /*============================================================================ 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 "mitkContourModelInteractor.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include mitk::ContourModelInteractor::ContourModelInteractor() { } void mitk::ContourModelInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick); CONNECT_CONDITION("mouseMove", IsHovering); CONNECT_FUNCTION("movePoint", OnMovePoint); CONNECT_FUNCTION("deletePoint", OnDeletePoint); + CONNECT_FUNCTION("addPoint", OnAddPoint); CONNECT_FUNCTION("finish", OnFinishEditing); } mitk::ContourModelInteractor::~ContourModelInteractor() { } void mitk::ContourModelInteractor::SetRestrictedArea(mitk::ContourModel* restrictedArea) { m_RestrictedArea = restrictedArea; } bool mitk::ContourModelInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return false; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); if (contour == nullptr) { MITK_ERROR << "Invalid Contour"; return false; } contour->Deselect(); mitk::Point3D click = positionEvent->GetPositionInWorld(); bool isVertexSelected = contour->SelectControlVertexAt(click, ContourModelInteractor::eps, timeStep); if (isVertexSelected) { auto foundVertex = contour->GetSelectedVertex(); const auto restrictedVs = m_RestrictedArea->GetVertexList(timeStep); for (auto restrictedV : restrictedVs) { if (restrictedV->Coordinates == foundVertex->Coordinates) { isVertexSelected = false; contour->Deselect(); break; } } } if (isVertexSelected) { mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } return isVertexSelected; } +void mitk::ContourModelInteractor::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (!positionEvent) + return; + + const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); + + auto* contour = dynamic_cast(this->GetDataNode()->GetData()); + + Point3D currentPosition = positionEvent->GetPositionInWorld(); + + ContourElement::VertexSizeType segmentStart; + ContourElement::VertexSizeType segmentEnd; + Point3D closestContourPoint; + if (contour->GetLineSegmentForPoint(currentPosition, ContourModelInteractor::eps, timeStep, segmentStart, segmentEnd, closestContourPoint, true)) + { + const auto vertexList = contour->GetVertexList(timeStep); + //check if the segment is NOT within restricted control points. + auto controlStartIt = vertexList.begin(); + auto controlEndIt = vertexList.begin(); + for (auto searchIt = vertexList.begin() + segmentStart; searchIt != vertexList.begin(); searchIt--) + { + if ((*searchIt)->IsControlPoint) + { + controlStartIt = searchIt; + break; + } + } + for (auto searchIt = vertexList.begin() + segmentEnd; searchIt != vertexList.end(); searchIt++) + { + if ((*searchIt)->IsControlPoint) + { + controlEndIt = searchIt; + break; + } + } + + const auto restrictedVs = m_RestrictedArea->GetVertexList(timeStep); + bool startIsRestricted = false; + bool stopIsRestricted = false; + + for (auto restrictedV : restrictedVs) + { + if (restrictedV->Coordinates == (*controlStartIt)->Coordinates) + { + startIsRestricted = true; + } + if (restrictedV->Coordinates == (*controlEndIt)->Coordinates) + { + stopIsRestricted = true; + } + } + + if (!(startIsRestricted && stopIsRestricted)) + { + //add the point + contour->InsertVertexAtIndex(closestContourPoint, segmentEnd, true, timeStep); + contour->SelectVertexAt(segmentEnd); + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); + } + } +} + void mitk::ContourModelInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *) { auto *contour = dynamic_cast(this->GetDataNode()->GetData()); contour->RemoveVertex(contour->GetSelectedVertex()); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } bool mitk::ContourModelInteractor::IsHovering(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return false; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); - mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); + Point3D currentPosition = positionEvent->GetPositionInWorld(); bool isHover = false; this->GetDataNode()->GetBoolProperty("contour.hovering", isHover, positionEvent->GetSender()); if (contour->IsNearContour(currentPosition, ContourModelInteractor::eps, timeStep)) { if (isHover == false) { this->GetDataNode()->SetBoolProperty("contour.hovering", true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } + //check if it would be valid to add a point. } else { if (isHover == true) { this->GetDataNode()->SetBoolProperty("contour.hovering", false); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } } this->m_lastMousePosition = currentPosition; return false; } void mitk::ContourModelInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; auto *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftSelectedVertex(translation); this->m_lastMousePosition = positionEvent->GetPositionInWorld(); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::ContourModelInteractor::OnMoveContour(StateMachineAction *, InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftContour(translation, timeStep); this->m_lastMousePosition = positionEvent->GetPositionInWorld(); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::ContourModelInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *) { } diff --git a/Modules/Segmentation/Interactions/mitkContourModelInteractor.h b/Modules/Segmentation/Interactions/mitkContourModelInteractor.h index c5f7ea1377..6c2e779f20 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourModelInteractor.h @@ -1,67 +1,68 @@ /*============================================================================ 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 mitkContourModelInteractor_h_Included #define mitkContourModelInteractor_h_Included #include "mitkCommon.h" #include "mitkDataInteractor.h" #include #include #include namespace mitk { /** \brief \sa Interactor \ingroup Interaction \ingroup ToolManagerEtAl */ class MITKSEGMENTATION_EXPORT ContourModelInteractor : public DataInteractor { public: mitkClassMacro(ContourModelInteractor, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; void SetRestrictedArea(mitk::ContourModel* restrictedArea); protected: ContourModelInteractor(); ~ContourModelInteractor() override; virtual bool OnCheckPointClick(const InteractionEvent *interactionEvent); virtual bool IsHovering(const InteractionEvent *interactionEvent); + virtual void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent); virtual void OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnMoveContour(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent); const float eps = 3.0; mitk::Point3D m_lastMousePosition; mitk::ContourModel::Pointer m_RestrictedArea; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp index 3ad0e2417e..6abfad07ac 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp @@ -1,327 +1,333 @@ /*============================================================================ 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 "mitkContourModelLiveWireInteractor.h" #include "mitkInteractionPositionEvent.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include "mitkIOUtil.h" mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor() : ContourModelInteractor() { m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetUseCostFunction(true); m_NextActiveVertexDown.Fill(0); m_NextActiveVertexUp.Fill(0); } mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor() { } void mitk::ContourModelLiveWireInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick); CONNECT_CONDITION("mouseMove", IsHovering); CONNECT_FUNCTION("movePoint", OnMovePoint); CONNECT_FUNCTION("deletePoint", OnDeletePoint); + CONNECT_FUNCTION("addPoint", OnAddPoint) CONNECT_FUNCTION("finish", OnFinishEditing); } bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent) { auto isVertexSelected = Superclass::OnCheckPointClick(interactionEvent); if (isVertexSelected) { auto* contour = dynamic_cast(this->GetDataNode()->GetData()); const auto* positionEvent = dynamic_cast(interactionEvent); mitk::Point3D click = positionEvent->GetPositionInWorld(); const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto controlVertices = contour->GetControlVertices(timeStep); const mitk::ContourModel::VertexType* nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); const mitk::ContourModel::VertexType* previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); this->SplitContourFromSelectedVertex(contour, nextPoint, previousPoint, timeStep); m_NextActiveVertexUp = nextPoint->Coordinates; m_NextActiveVertexDown = previousPoint->Coordinates; // clear previous void positions this->m_LiveWireFilter->ClearRepulsivePoints(); // all points in lower and upper part should be marked as repulsive points to not be changed this->SetRepulsivePoints(previousPoint, m_ContourLeft, timeStep); this->SetRepulsivePoints(nextPoint, m_ContourRight, timeStep); // clear container with void points between neighboring control points m_ContourBeingModified.clear(); } return isVertexSelected; } void mitk::ContourModelLiveWireInteractor::SetWorkingImage(mitk::Image *_arg) { if (this->m_WorkingSlice != _arg) { this->m_WorkingSlice = _arg; this->m_LiveWireFilter->SetInput(this->m_WorkingSlice); } } +void mitk::ContourModelLiveWireInteractor::OnAddPoint(StateMachineAction* sm, InteractionEvent* interactionEvent) +{ + Superclass::OnAddPoint(sm, interactionEvent); +} + void mitk::ContourModelLiveWireInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); if (contour == nullptr) { MITK_ERROR << "Invalid Contour!"; return; } if (contour->GetSelectedVertex()) { mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone()); newContour->Concatenate(m_ContourLeft, timeStep); // recompute contour between neighbored two active control points this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown); this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp); this->m_LiveWireFilter->Update(); mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput(); assert(liveWireContour); if (liveWireContour->IsEmpty(timeStep)) return; liveWireContour->RemoveVertexAt(0, timeStep); liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timeStep) - 1, timeStep); // insert new live wire computed points newContour->Concatenate(liveWireContour, timeStep); // insert right side of original contour newContour->Concatenate(this->m_ContourRight, timeStep); newContour->SetClosed(contour->IsClosed(timeStep), timeStep); // instead of leaving a single point, delete all points if (newContour->GetNumberOfVertices(timeStep) <= 2) { newContour->Clear(timeStep); } this->GetDataNode()->SetData(newContour); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } } void mitk::ContourModelLiveWireInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); if (contour == nullptr) { MITK_ERROR << "invalid contour"; return; } std::cout << currentPosition << std::endl; mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New(); editingContour->Expand(contour->GetTimeSteps()); editingContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone()); // recompute left live wire, i.e. the contour between previous active vertex and selected vertex this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown); this->m_LiveWireFilter->SetEndPoint(currentPosition); // remove void positions between previous active vertex and next active vertex. if (!m_ContourBeingModified.empty()) { std::vector>::const_iterator iter = m_ContourBeingModified.begin(); for (; iter != m_ContourBeingModified.end(); iter++) { this->m_LiveWireFilter->RemoveRepulsivePoint((*iter)); } } // update to get the left livewire. Remember that the points in the rest of the contour are already // set as void positions in the filter this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput(); assert(leftLiveWire); if (!leftLiveWire->IsEmpty(timeStep)) leftLiveWire->RemoveVertexAt(0, timeStep); editingContour->Concatenate(leftLiveWire, timeStep); // the new index of the selected vertex unsigned int selectedVertexIndex = this->m_ContourLeft->GetNumberOfVertices(timeStep) + leftLiveWire->GetNumberOfVertices(timeStep) - 1; // at this point the container has to be empty m_ContourBeingModified.clear(); // add points from left live wire contour auto iter = leftLiveWire->IteratorBegin(timeStep); for (; iter != leftLiveWire->IteratorEnd(timeStep); iter++) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); // add indices m_ContourBeingModified.push_back(idx); } // recompute right live wire, i.e. the contour between selected vertex and next active vertex this->m_LiveWireFilter->SetStartPoint(currentPosition); this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp); // update filter with all contour points set as void but the right live wire portion to be calculated now this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput(); assert(rightLiveWire); if (!leftLiveWire->IsEmpty(timeStep)) leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timeStep); if (!rightLiveWire->IsEmpty(timeStep)) rightLiveWire->RemoveVertexAt(0, timeStep); editingContour->Concatenate(rightLiveWire, timeStep); mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone()); // concatenate left original contour newContour->Concatenate(this->m_ContourLeft, timeStep); newContour->Concatenate(editingContour, timeStep, true); // set last inserted vertex as selected newContour->SelectVertexAt(selectedVertexIndex, timeStep); // set as control point newContour->SetSelectedVertexAsControlPoint(true); // concatenate right original contour newContour->Concatenate(this->m_ContourRight, timeStep); newContour->SetClosed(contour->IsClosed(timeStep), timeStep); this->GetDataNode()->SetData(newContour); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel *srcContour, const mitk::ContourModel::VertexType *nextPoint, const mitk::ContourModel::VertexType *previousPoint, int timeStep) { m_ContourLeft = mitk::ContourModel::New(); m_ContourRight = mitk::ContourModel::New(); auto it = srcContour->IteratorBegin(); // part between nextPoint and end of Countour bool upperPart = false; // part between start of countour and previousPoint bool lowerPart = true; // edge cases when point right before first control vertex is selected or first control vertex is selected if (nextPoint == (*it) || srcContour->GetSelectedVertex() == (*it)) { upperPart = true; lowerPart = false; m_ContourLeft->AddVertex(previousPoint->Coordinates, previousPoint->IsControlPoint, timeStep); } // if first control vertex is selected, move to next point before adding vertices to m_ContourRight // otherwise, second line appears when moving the vertex if (srcContour->GetSelectedVertex() == (*it)) { while (*it != nextPoint) { it++; } } for (; it != srcContour->IteratorEnd(timeStep); it++) { // everything in lower part should be added to m_CountoutLeft if (lowerPart) { m_ContourLeft->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep); } // start of "restricted area" where no vertex should be added to m_CountoutLeft or m_CountoutRight if (*it == previousPoint) { lowerPart = false; upperPart = false; } // start of upperPart if (*it == nextPoint) { upperPart = true; } // everything in upper part should be added to m_CountoutRight if (upperPart) { m_ContourRight->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep); } } } void mitk::ContourModelLiveWireInteractor::SetRepulsivePoints(const mitk::ContourModel::VertexType *pointToExclude, mitk::ContourModel *contour, int timeStep) { auto it = contour->IteratorBegin(); for (; it != contour->IteratorEnd(timeStep); it++) { if (*it != pointToExclude) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*it)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); } } } void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) { } diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h index dac2bf9fbc..e9ba614f3b 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h @@ -1,89 +1,91 @@ /*============================================================================ 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 mitkContourModelLiveWireInteractor_h_Included #define mitkContourModelLiveWireInteractor_h_Included #include "mitkCommon.h" #include "mitkContourModelInteractor.h" #include #include namespace mitk { /** \brief \sa Interactor \sa ContourModelInteractor \ingroup Interaction \warning Make sure the working image is properly set, otherwise the algorithm for computing livewire contour segments will not work! */ class MITKSEGMENTATION_EXPORT ContourModelLiveWireInteractor : public ContourModelInteractor { public: mitkClassMacro(ContourModelLiveWireInteractor, ContourModelInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); virtual void SetWorkingImage(mitk::Image *_arg); void ConnectActionsAndFunctions() override; protected: ContourModelLiveWireInteractor(); ~ContourModelLiveWireInteractor() override; /// \brief Select/ add and select vertex to modify contour and prepare for modification of contour. bool OnCheckPointClick(const InteractionEvent *interactionEvent) override; /// \brief Update contour when point is moved. void OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; + /// \brief Add a new control point. + void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) override; /// \brief Delete selected vertex and recompute contour. void OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; /// \brief Finish modification of contour. void OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) override; /// \brief Split contour into a part before the selected vertex and after the selected vertex void SplitContourFromSelectedVertex(mitk::ContourModel *srcContour, const mitk::ContourModel::VertexType *nextPoint, const mitk::ContourModel::VertexType *previousPoint, int timestep); /// \brief Set repulsive points which should not be changed during editing of the contour. void SetRepulsivePoints(const mitk::ContourModel::VertexType *nextPoint, mitk::ContourModel *contour, int timestep); mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter; mitk::Image::Pointer m_WorkingSlice; mitk::Point3D m_NextActiveVertexDown; mitk::Point3D m_NextActiveVertexUp; mitk::ContourModel::VertexIterator m_NextActiveVertexDownIter; mitk::ContourModel::VertexIterator m_NextActiveVertexUpIter; std::vector> m_ContourBeingModified; mitk::ContourModel::Pointer m_ContourLeft; mitk::ContourModel::Pointer m_ContourRight; }; } // namespace mitk #endif // mitkContourModelLiveWireInteractor_h_Included diff --git a/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp b/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp index c6087faa44..219bd00779 100644 --- a/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp @@ -1,436 +1,459 @@ /*============================================================================ 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 "mitkEditableContourTool.h" #include mitk::EditableContourTool::EditableContourTool() : FeedbackContourTool("EditableContourTool") {} mitk::EditableContourTool::~EditableContourTool() { this->ReleaseHelperObjects(); this->ReleaseInteractors(); } void mitk::EditableContourTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("InitObject", OnInitContour); CONNECT_FUNCTION("AddPoint", OnAddPoint); CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); CONNECT_FUNCTION("Drawing", OnDrawing); CONNECT_FUNCTION("EndDrawing", OnEndDrawing); CONNECT_FUNCTION("FinishContour", OnFinish); CONNECT_FUNCTION("CtrlMovePoint", OnMouseMoved); } void mitk::EditableContourTool::Activated() { Superclass::Activated(); this->ResetToStartState(); this->EnableContourInteraction(true); } void mitk::EditableContourTool::Deactivated() { this->ClearSegmentation(); Superclass::Deactivated(); } void mitk::EditableContourTool::ConfirmSegmentation(bool resetStatMachine) { auto referenceImage = this->GetReferenceData(); auto workingImage = this->GetWorkingData(); if (nullptr != referenceImage && nullptr != workingImage) { std::vector sliceInfos; const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); TimeStepType workingImageTimeStep = workingImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); - if (m_Contour.IsNull() || m_Contour->IsEmpty()) + auto contour = this->GetContour(); + if (nullptr == contour || contour->IsEmpty()) return; auto workingSlice = this->GetAffectedImageSliceAs2DImage(m_PlaneGeometry, workingImage, workingImageTimeStep)->Clone(); sliceInfos.emplace_back(workingSlice, m_PlaneGeometry, workingImageTimeStep); - auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, m_Contour); + auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contour); int activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage); if (!m_AddMode) { activePixelValue = 0; } ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, activePixelValue); this->WriteBackSegmentationResults(sliceInfos); } this->ReleaseHelperObjects(); this->ReleaseInteractors(); if (resetStatMachine) this->ResetToStartState(); } void mitk::EditableContourTool::ClearSegmentation() { this->ReleaseHelperObjects(); this->ReleaseInteractors(); this->ResetToStartState(); } bool mitk::EditableContourTool::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent *positionEvent, mitk::BaseData *data) { bool isPositionEventInsideImageRegion = nullptr != data && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); if (!isPositionEventInsideImageRegion) MITK_WARN("EditableContourTool") << "PositionEvent is outside ImageRegion!"; return isPositionEventInsideImageRegion; } mitk::Point3D mitk::EditableContourTool::PrepareInitContour(const Point3D& clickedPoint) { //default implementation does nothing return clickedPoint; } void mitk::EditableContourTool::OnInitContour(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; auto workingDataNode = this->GetWorkingDataNode(); - if (m_Contour.IsNotNull()) + if (nullptr != this->GetContour()) { this->ConfirmSegmentation(false); } if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) { this->ResetToStartState(); return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); - m_Contour = this->CreateNewContour(); + auto contour = this->CreateNewContour(); m_ContourNode = mitk::DataNode::New(); - m_ContourNode->SetData(m_Contour); + m_ContourNode->SetData(contour); m_ContourNode->SetName("working contour node"); m_ContourNode->SetProperty("layer", IntProperty::New(100)); m_ContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_ContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ContourNode->AddProperty("contour.color", ColorProperty::New(1.0f, 1.0f, 0.0f), nullptr, true); m_ContourNode->AddProperty("contour.points.color", ColorProperty::New(1.0f, 0.0f, 0.1f), nullptr, true); m_ContourNode->AddProperty("contour.controlpoints.show", BoolProperty::New(true), nullptr, true); m_PreviewContour = this->CreateNewContour(); m_PreviewContourNode = mitk::DataNode::New(); m_PreviewContourNode->SetData(m_PreviewContour); m_PreviewContourNode->SetName("active preview node"); m_PreviewContourNode->SetProperty("layer", IntProperty::New(101)); m_PreviewContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_PreviewContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PreviewContourNode->AddProperty("contour.color", ColorProperty::New(0.1f, 1.0f, 0.1f), nullptr, true); m_PreviewContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0f), nullptr, true); m_ClosureContour = this->CreateNewContour(); m_ClosureContourNode = mitk::DataNode::New(); m_ClosureContourNode->SetData(m_ClosureContour); m_ClosureContourNode->SetName("active closure node"); m_ClosureContourNode->SetProperty("layer", IntProperty::New(101)); m_ClosureContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_ClosureContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ClosureContourNode->AddProperty("contour.color", ColorProperty::New(0.0f, 1.0f, 0.1f), nullptr, true); m_ClosureContourNode->AddProperty("contour.width", mitk::FloatProperty::New(2.0f), nullptr, true); m_CurrentRestrictedArea = this->CreateNewContour(); auto dataStorage = this->GetToolManager()->GetDataStorage(); dataStorage->Add(m_ContourNode, workingDataNode); dataStorage->Add(m_PreviewContourNode, workingDataNode); dataStorage->Add(m_ClosureContourNode, workingDataNode); m_ReferenceDataSlice = this->GetAffectedReferenceSlice(positionEvent); auto origin = m_ReferenceDataSlice->GetSlicedGeometry()->GetOrigin(); m_ReferenceDataSlice->GetSlicedGeometry()->WorldToIndex(origin, origin); m_ReferenceDataSlice->GetSlicedGeometry()->IndexToWorld(origin, origin); m_ReferenceDataSlice->GetSlicedGeometry()->SetOrigin(origin); // Remember PlaneGeometry to determine if events were triggered in the same plane m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); // Map click to pixel coordinates auto click = positionEvent->GetPositionInWorld(); click = this->PrepareInitContour(click); this->InitializePreviewContour(click); // Set initial start point - m_Contour->AddVertex(click, true); + contour->AddVertex(click, true); m_PreviewContour->AddVertex(click, false); m_ClosureContour->AddVertex(click); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::EditableContourTool::FinalizePreviewContour(const Point3D& clickedPoint) -{ // Remove duplicate first vertex, it's already contained in m_Contour +{ // Remove duplicate first vertex, it's already contained in m_ContourNode m_PreviewContour->RemoveVertexAt(0); m_PreviewContour->SetControlVertexAt(m_PreviewContour->GetNumberOfVertices() - 1); } void mitk::EditableContourTool::InitializePreviewContour(const Point3D& clickedPoint) { //default implementation only clears the preview and sets the start point m_PreviewContour = this->CreateNewContour(); m_PreviewContour->AddVertex(clickedPoint); m_PreviewContourNode->SetData(m_PreviewContour); } void mitk::EditableContourTool::UpdatePreviewContour(const Point3D& clickedPoint) { //default implementation draws just a simple line to position if (m_PreviewContour->GetNumberOfVertices() > 2) { - this->InitializePreviewContour(m_Contour->GetVertexAt(m_Contour->GetNumberOfVertices() - 1)->Coordinates); + auto contour = this->GetContour(); + this->InitializePreviewContour(contour->GetVertexAt(contour->GetNumberOfVertices() - 1)->Coordinates); } if (m_PreviewContour->GetNumberOfVertices() == 2) { m_PreviewContour->RemoveVertexAt(m_PreviewContour->GetNumberOfVertices()-1); } m_PreviewContour->AddVertex(clickedPoint); } void mitk::EditableContourTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; if (m_PlaneGeometry.IsNotNull()) { // Check if the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) return; } this->FinalizePreviewContour(positionEvent->GetPositionInWorld()); // Merge contours - m_Contour->Concatenate(m_PreviewContour); + auto contour = this->GetContour(); + contour->Concatenate(m_PreviewContour); this->InitializePreviewContour(positionEvent->GetPositionInWorld()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::EditableContourTool::OnDrawing(StateMachineAction*, InteractionEvent* interactionEvent) { auto* positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; m_PreviewContourNode->SetVisibility(false); - m_Contour->AddVertex(positionEvent->GetPositionInWorld(), false); + auto contour = this->GetContour(); + contour->AddVertex(positionEvent->GetPositionInWorld(), false); UpdateClosureContour(positionEvent->GetPositionInWorld()); assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::EditableContourTool::OnEndDrawing(StateMachineAction*, InteractionEvent* interactionEvent) { auto* positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; - auto controlVs = m_Contour->GetControlVertices(0); + auto contour = this->GetContour(); + auto controlVs = contour->GetControlVertices(0); if (!controlVs.empty()) { //add the last control point (after that the draw part start) m_CurrentRestrictedArea->AddVertex(controlVs.back()->Coordinates); } m_PreviewContourNode->SetVisibility(true); - m_Contour->SetControlVertexAt(m_Contour->GetNumberOfVertices() - 1); + contour->SetControlVertexAt(contour->GetNumberOfVertices() - 1); //add the just created/set last control point (with it the draw part ends) - m_CurrentRestrictedArea->AddVertex(m_Contour->GetVertexAt(m_Contour->GetNumberOfVertices() - 1)->Coordinates); + m_CurrentRestrictedArea->AddVertex(contour->GetVertexAt(contour->GetNumberOfVertices() - 1)->Coordinates); this->InitializePreviewContour(positionEvent->GetPositionInWorld()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::EditableContourTool::OnMouseMoved(StateMachineAction*, InteractionEvent* interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; if (m_PlaneGeometry.IsNotNull()) { // Check if the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) return; } this->UpdatePreviewContour(positionEvent->GetPositionInWorld()); this->UpdateClosureContour(positionEvent->GetPositionInWorld()); RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::EditableContourTool::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; if (m_PlaneGeometry.IsNotNull()) { // Check if the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) return; } this->FinalizePreviewContour(positionEvent->GetPositionInWorld()); this->FinishTool(); // Merge contours - m_Contour->Concatenate(m_PreviewContour); + auto contour = this->GetContour(); + contour->Concatenate(m_PreviewContour); - auto numberOfTimesteps = static_cast(m_Contour->GetTimeSteps()); + auto numberOfTimesteps = static_cast(contour->GetTimeSteps()); for (int i = 0; i <= numberOfTimesteps; ++i) - m_Contour->Close(i); + contour->Close(i); this->ReleaseHelperObjects(false); if (m_AutoConfirm) { this->ConfirmSegmentation(); } } void mitk::EditableContourTool::ReleaseHelperObjects(bool includeWorkingContour) { this->RemoveHelperObjectsFromDataStorage(includeWorkingContour); if (includeWorkingContour) { m_ContourNode = nullptr; - m_Contour = nullptr; - m_CurrentRestrictedArea = nullptr; } m_PreviewContourNode = nullptr; m_PreviewContour = nullptr; m_ClosureContourNode = nullptr; m_ClosureContour = nullptr; } void mitk::EditableContourTool::RemoveHelperObjectsFromDataStorage(bool includeWorkingContour) { auto dataStorage = this->GetToolManager()->GetDataStorage(); if (nullptr == dataStorage) return; if (includeWorkingContour) { if (m_ContourNode.IsNotNull()) dataStorage->Remove(m_ContourNode); } if (m_PreviewContourNode.IsNotNull()) dataStorage->Remove(m_PreviewContourNode); if (m_ClosureContourNode.IsNotNull()) dataStorage->Remove(m_ClosureContourNode); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::ContourModel::Pointer mitk::EditableContourTool::CreateNewContour() const { auto workingData = this->GetWorkingData(); if (nullptr == workingData) { this->InteractiveSegmentationBugMessage( "Cannot create new contour. No valid working data is set. Application is in invalid state."); mitkThrow() << "Cannot create new contour. No valid working data is set. Application is in invalid state."; } auto contour = ContourModel::New(); // generate a time geometry that is always visible as the working contour should always be. auto contourTimeGeometry = ProportionalTimeGeometry::New(); contourTimeGeometry->SetStepDuration(std::numeric_limits::max()); contourTimeGeometry->SetTimeStepGeometry(contour->GetTimeGeometry()->GetGeometryForTimeStep(0)->Clone(), 0); contour->SetTimeGeometry(contourTimeGeometry); return contour; } void mitk::EditableContourTool::UpdateClosureContour(mitk::Point3D endpoint) { if (m_ClosureContour->GetNumberOfVertices() > 2) { m_ClosureContour = this->CreateNewContour(); m_ClosureContourNode->SetData(m_ClosureContour); } if (m_ClosureContour->GetNumberOfVertices() == 0) { - m_ClosureContour->AddVertex(m_Contour->GetVertexAt(0)->Coordinates); + auto contour = this->GetContour(); + m_ClosureContour->AddVertex(contour->GetVertexAt(0)->Coordinates); m_ClosureContour->Update(); } if (m_ClosureContour->GetNumberOfVertices() == 2) { m_ClosureContour->RemoveVertexAt(0); } m_ClosureContour->AddVertexAtFront(endpoint); } void mitk::EditableContourTool::EnableContourInteraction(bool on) { if (m_ContourInteractor.IsNotNull()) { m_ContourInteractor->EnableInteraction(on); } } void mitk::EditableContourTool::ReleaseInteractors() { this->EnableContourInteraction(false); m_ContourInteractor = nullptr; } + +mitk::ContourModel* mitk::EditableContourTool::GetContour() +{ + if (this->m_ContourNode.IsNotNull()) + { + return dynamic_cast(this->m_ContourNode->GetData()); + } + return nullptr; +}; + +const mitk::ContourModel* mitk::EditableContourTool::GetContour() const +{ + if (this->m_ContourNode.IsNotNull()) + { + return dynamic_cast(this->m_ContourNode->GetData()); + } + return nullptr; +}; \ No newline at end of file diff --git a/Modules/Segmentation/Interactions/mitkEditableContourTool.h b/Modules/Segmentation/Interactions/mitkEditableContourTool.h index 6b507eee4b..8abc1a57f4 100644 --- a/Modules/Segmentation/Interactions/mitkEditableContourTool.h +++ b/Modules/Segmentation/Interactions/mitkEditableContourTool.h @@ -1,112 +1,113 @@ /*============================================================================ 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 mitkEditbaleContourTool_h_Included #define mitkEditbaleContourTool_h_Included #include namespace mitk { class MITKSEGMENTATION_EXPORT EditableContourTool : public FeedbackContourTool { public: mitkClassMacro(EditableContourTool, FeedbackContourTool); /// \brief Convert current contour to binary segmentations. virtual void ConfirmSegmentation(bool resetStatMachine = true); /// \brief Delete all current contours. virtual void ClearSegmentation(); itkBooleanMacro(AutoConfirm); itkSetMacro(AutoConfirm, bool); itkGetMacro(AutoConfirm, bool); itkBooleanMacro(AddMode); itkSetMacro(AddMode, bool); itkGetMacro(AddMode, bool); protected: EditableContourTool(); ~EditableContourTool() override; void ConnectActionsAndFunctions() override; void Activated() override; void Deactivated() override; virtual Point3D PrepareInitContour(const Point3D& clickedPoint); virtual void FinalizePreviewContour(const Point3D& clickedPoint); virtual void InitializePreviewContour(const Point3D& clickedPoint); virtual void UpdatePreviewContour(const Point3D& clickedPoint); /// \brief Initialize tool. virtual void OnInitContour(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Add a control point and finish current segment. virtual void OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Draw a contour according to the mouse movement when mouse button is pressed and mouse is moved. virtual void OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnEndDrawing(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Computation of the preview contour. virtual void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Finish EditableContour tool. virtual void OnFinish(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Finish contour interaction. virtual void FinishTool() = 0; void EnableContourInteraction(bool on); void ReleaseInteractors(); virtual void ReleaseHelperObjects(bool includeWorkingContour = true); virtual void RemoveHelperObjectsFromDataStorage(bool includeWorkingContour = true); ContourModel::Pointer CreateNewContour() const; virtual void UpdateClosureContour(mitk::Point3D endpoint); bool IsPositionEventInsideImageRegion(InteractionPositionEvent *positionEvent, BaseData *data); - mitk::ContourModel::Pointer m_Contour; + mitk::ContourModel* GetContour(); + const mitk::ContourModel* GetContour() const; mitk::DataNode::Pointer m_ContourNode; mitk::ContourModel::Pointer m_PreviewContour; mitk::DataNode::Pointer m_PreviewContourNode; mitk::ContourModel::Pointer m_ClosureContour; mitk::DataNode::Pointer m_ClosureContourNode; mitk::ContourModel::Pointer m_CurrentRestrictedArea; /** Slice of the reference data the tool is currently actively working on to define contours.*/ mitk::Image::Pointer m_ReferenceDataSlice; PlaneGeometry::ConstPointer m_PlaneGeometry; mitk::DataInteractor::Pointer m_ContourInteractor; bool m_AutoConfirm; bool m_AddMode; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index 27bf3bfda1..861f2bd557 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,259 +1,261 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LiveWireTool2D, "LiveWire tool"); } mitk::LiveWireTool2D::LiveWireTool2D() : EditableContourTool(), m_CreateAndUseDynamicCosts(false) { } mitk::LiveWireTool2D::~LiveWireTool2D() { } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { mitk::EditableContourTool::ConnectActionsAndFunctions(); CONNECT_FUNCTION("MovePoint", OnMouseMoveNoDynamicCosts); } const char **mitk::LiveWireTool2D::GetXPM() const { return mitkLiveWireTool2D_xpm; } us::ModuleResource mitk::LiveWireTool2D::GetIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_48x48.png"); } us::ModuleResource mitk::LiveWireTool2D::GetCursorIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_Cursor_32x32.png"); } const char *mitk::LiveWireTool2D::GetName() const { return "Live Wire"; } void mitk::LiveWireTool2D::UpdateLiveWireContour() { - if (m_Contour.IsNotNull()) + auto contour = this->GetContour(); + if (nullptr != contour) { - auto timeGeometry = m_Contour->GetTimeGeometry()->Clone(); + auto timeGeometry = contour->GetTimeGeometry()->Clone(); m_PreviewContour = this->m_LiveWireFilter->GetOutput(); // needed because the results of the filter are always from 0 ms // to 1 ms and the filter also resets its outputs. // generate a time geometry that is always visible as the working contour should always be. auto contourTimeGeometry = ProportionalTimeGeometry::New(); contourTimeGeometry->SetStepDuration(std::numeric_limits::max()); - contourTimeGeometry->SetTimeStepGeometry(m_Contour->GetTimeGeometry()->GetGeometryForTimeStep(0)->Clone(), 0); + contourTimeGeometry->SetTimeStepGeometry(contour->GetTimeGeometry()->GetGeometryForTimeStep(0)->Clone(), 0); m_PreviewContour->SetTimeGeometry(contourTimeGeometry); m_PreviewContourNode->SetData(this->m_PreviewContour); } } void mitk::LiveWireTool2D::OnTimePointChanged() { auto reference = this->GetReferenceData(); if (nullptr == reference || m_PlaneGeometry.IsNull() || m_LiveWireFilter.IsNull() || m_PreviewContourNode.IsNull()) return; auto timeStep = reference->GetTimeGeometry()->TimePointToTimeStep(this->GetLastTimePointTriggered()); m_ReferenceDataSlice = GetAffectedImageSliceAs2DImageByTimePoint(m_PlaneGeometry, reference, timeStep); m_LiveWireFilter->SetInput(m_ReferenceDataSlice); m_LiveWireFilter->Update(); this->UpdateLiveWireContour(); RenderingManager::GetInstance()->RequestUpdateAll(); }; mitk::Point3D mitk::LiveWireTool2D::PrepareInitContour(const mitk::Point3D& clickedPoint) { // Set current slice as input for ImageToLiveWireContourFilter m_LiveWireFilter = ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetUseCostFunction(true); m_LiveWireFilter->SetInput(m_ReferenceDataSlice); itk::Index<3> idx; m_ReferenceDataSlice->GetGeometry()->WorldToIndex(clickedPoint, idx); // Get the pixel with the highest gradient in a 7x7 region itk::Index<3> indexWithHighestGradient; AccessFixedDimensionByItk_2(m_ReferenceDataSlice, FindHighestGradientMagnitudeByITK, 2, idx, indexWithHighestGradient); Point3D adaptedClick; m_ReferenceDataSlice->GetGeometry()->IndexToWorld(indexWithHighestGradient, adaptedClick); m_CreateAndUseDynamicCosts = true; return adaptedClick; } void mitk::LiveWireTool2D::FinalizePreviewContour(const Point3D& clickedPoint) { // Add repulsive points to avoid getting the same path again std::for_each(m_PreviewContour->IteratorBegin(), m_PreviewContour->IteratorEnd(), [this](ContourElement::VertexType* vertex) { ImageLiveWireContourModelFilter::InternalImageType::IndexType idx; this->m_ReferenceDataSlice->GetGeometry()->WorldToIndex(vertex->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); }); EditableContourTool::FinalizePreviewContour(clickedPoint); } void mitk::LiveWireTool2D::InitializePreviewContour(const Point3D& clickedPoint) { m_PreviewContour->Clear(); // Set new start point m_LiveWireFilter->SetStartPoint(clickedPoint); if (m_CreateAndUseDynamicCosts) { + auto contour = this->GetContour(); // Use dynamic cost map for next update - m_LiveWireFilter->CreateDynamicCostMap(m_Contour); + m_LiveWireFilter->CreateDynamicCostMap(contour); m_LiveWireFilter->SetUseDynamicCostMap(true); } } void mitk::LiveWireTool2D::UpdatePreviewContour(const Point3D& clickedPoint) { // Compute LiveWire segment from last control point to current mouse position m_LiveWireFilter->SetEndPoint(clickedPoint); m_LiveWireFilter->Update(); this->UpdateLiveWireContour(); } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent) { m_LiveWireFilter->SetUseDynamicCostMap(false); this->OnMouseMoved(nullptr, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); } void mitk::LiveWireTool2D::FinishTool() { m_LiveWireFilter->SetUseDynamicCostMap(false); m_ContourInteractor = mitk::ContourModelLiveWireInteractor::New(); m_ContourInteractor->SetDataNode(m_ContourNode); m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); m_ContourInteractor->SetWorkingImage(this->m_ReferenceDataSlice); m_ContourInteractor->SetRestrictedArea(this->m_CurrentRestrictedArea); m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); } template void mitk::LiveWireTool2D::FindHighestGradientMagnitudeByITK(itk::Image *inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; const auto MAX_X = inputImage->GetLargestPossibleRegion().GetSize()[0]; const auto MAX_Y = inputImage->GetLargestPossibleRegion().GetSize()[1]; returnIndex[0] = index[0]; returnIndex[1] = index[1]; returnIndex[2] = 0.0; double gradientMagnitude = 0.0; double maxGradientMagnitude = 0.0; // The size and thus the region of 7x7 is only used to calculate the gradient magnitude in that region, // not for searching the maximum value. // Maximum value in each direction for size typename InputImageType::SizeType size; size[0] = 7; size[1] = 7; // Minimum value in each direction for startRegion IndexType startRegion; startRegion[0] = index[0] - 3; startRegion[1] = index[1] - 3; if (startRegion[0] < 0) startRegion[0] = 0; if (startRegion[1] < 0) startRegion[1] = 0; if (MAX_X - index[0] < 7) startRegion[0] = MAX_X - 7; if (MAX_Y - index[1] < 7) startRegion[1] = MAX_Y - 7; index[0] = startRegion[0] + 3; index[1] = startRegion[1] + 3; typename InputImageType::RegionType region; region.SetSize(size); region.SetIndex(startRegion); typedef typename itk::GradientMagnitudeImageFilter GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(inputImage); gradientFilter->GetOutput()->SetRequestedRegion(region); gradientFilter->Update(); typename InputImageType::Pointer gradientMagnitudeImage; gradientMagnitudeImage = gradientFilter->GetOutput(); IndexType currentIndex; currentIndex[0] = 0; currentIndex[1] = 0; // Search max (approximate) gradient magnitude for (int x = -1; x <= 1; ++x) { currentIndex[0] = index[0] + x; for (int y = -1; y <= 1; ++y) { currentIndex[1] = index[1] + y; gradientMagnitude = gradientMagnitudeImage->GetPixel(currentIndex); // Check for new max if (maxGradientMagnitude < gradientMagnitude) { maxGradientMagnitude = gradientMagnitude; returnIndex[0] = currentIndex[0]; returnIndex[1] = currentIndex[1]; returnIndex[2] = 0.0; } } currentIndex[1] = index[1]; } } diff --git a/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml b/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml index da40ab89b3..fd6317e057 100644 --- a/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml +++ b/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml @@ -1,15 +1,22 @@ + + + + + + + diff --git a/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml b/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml index 32a7574791..4963143426 100644 --- a/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml +++ b/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml @@ -1,22 +1,28 @@ + + + + + + diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index a02175f7cd..6a732ab80a 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,80 +1,81 @@ set( CPP_FILES 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/QmitkEditableContourToolGUIBase.cpp Qmitk/QmitkLiveWireTool2DGUI.cpp Qmitk/QmitkNewAddTool2DGUI.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/QmitknnUNetWorker.cpp Qmitk/QmitkSurfaceStampWidget.cpp Qmitk/QmitkMaskStampWidget.cpp Qmitk/QmitkSliceBasedInterpolatorWidget.cpp Qmitk/QmitkStaticDynamicSegmentationDialog.cpp Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp Qmitk/QmitkSimpleLabelSetListWidget.cpp ) set(MOC_H_FILES 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/QmitkEditableContourToolGUIBase.h Qmitk/QmitkLiveWireTool2DGUI.h Qmitk/QmitkNewAddTool2DGUI.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/QmitknnUNetWorker.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/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkOtsuToolWidgetControls.ui -Qmitk/QmitkLiveWireTool2DGUIControls.ui -Qmitk/QmitkNewAddTool2DGUIControls.ui Qmitk/QmitkSurfaceStampWidgetGUIControls.ui Qmitk/QmitkMaskStampWidgetGUIControls.ui Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitknnUNetToolGUIControls.ui +Qmitk/QmitkEditableContourToolGUIControls.ui ) set(QRC_FILES resources/SegmentationUI.qrc )