diff --git a/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp b/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp index c40e3ba602..ac33b032f5 100644 --- a/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp @@ -1,322 +1,425 @@ /*============================================================================ 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->ConfirmSegmentation(); Superclass::Deactivated(); } -void mitk::EditableContourTool::ConfirmSegmentation() +void mitk::EditableContourTool::ConfirmSegmentation(bool resetStatMachine) { auto referenceImage = this->GetReferenceData(); auto workingImage = this->GetWorkingData(); if (nullptr != referenceImage && nullptr != workingImage) { std::vector sliceInfos; - sliceInfos.reserve(m_WorkingContours.size()); const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); TimeStepType workingImageTimeStep = workingImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); - for (const auto &workingContour : m_WorkingContours) - { - auto contour = dynamic_cast(workingContour.first->GetData()); - - if (nullptr == contour || contour->IsEmpty()) - continue; - - auto sameSlicePredicate = [&workingContour, workingImageTimeStep](const SliceInformation &si) { - return workingContour.second->IsOnPlane(si.plane) && workingImageTimeStep == si.timestep; - }; - - auto finding = std::find_if(sliceInfos.begin(), sliceInfos.end(), sameSlicePredicate); - if (finding == sliceInfos.end()) - { - auto workingSlice = - this->GetAffectedImageSliceAs2DImage(workingContour.second, workingImage, workingImageTimeStep)->Clone(); - sliceInfos.emplace_back(workingSlice, workingContour.second, workingImageTimeStep); - finding = std::prev(sliceInfos.end()); - } + if (m_Contour.IsNull() || m_Contour->IsEmpty()) + return; - // cast const away is OK in this case, because these are all slices created and manipulated - // localy in this function call. And we want to keep the high constness of SliceInformation for - // public interfaces. - auto workingSlice = const_cast(finding->slice.GetPointer()); + auto workingSlice = + this->GetAffectedImageSliceAs2DImage(m_PlaneGeometry, workingImage, workingImageTimeStep)->Clone(); + sliceInfos.emplace_back(workingSlice, m_PlaneGeometry, workingImageTimeStep); - auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contour); - int activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage); + auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, m_Contour); + int activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage); - ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, activePixelValue); - } + ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, activePixelValue); this->WriteBackSegmentationResults(sliceInfos); } - this->ClearSegmentation(); + 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()) + { + this->ConfirmSegmentation(false); + } + if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) { this->ResetToStartState(); return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_Contour = this->CreateNewContour(); m_ContourNode = mitk::DataNode::New(); m_ContourNode->SetData(m_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_EditingContour = this->CreateNewContour(); - m_EditingContourNode = mitk::DataNode::New(); - m_EditingContourNode->SetData(m_EditingContour); - m_EditingContourNode->SetName("editing node"); - m_EditingContourNode->SetProperty("layer", IntProperty::New(102)); - m_EditingContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); - m_EditingContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - m_EditingContourNode->AddProperty("contour.color", ColorProperty::New(0.1f, 1.0f, 0.1f), nullptr, true); - m_EditingContourNode->AddProperty("contour.points.color", ColorProperty::New(0.0f, 0.0f, 1.0f), nullptr, true); - m_EditingContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.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); - dataStorage->Add(m_EditingContourNode, 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); + 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 + 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::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) +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); + } + + 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); + 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; } - // remove green connection between mouse position and start point - m_ClosureContour->Clear(); + this->FinalizePreviewContour(positionEvent->GetPositionInWorld()); + + // Merge contours + m_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; - // Save contour and corresponding plane geometry to list - this->m_WorkingContours.emplace_back(std::make_pair(m_ContourNode, positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone())); - this->m_EditingContours.emplace_back(std::make_pair(m_EditingContourNode, positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone())); + m_PreviewContourNode->SetVisibility(false); + + m_Contour->AddVertex(positionEvent->GetPositionInWorld(), false); + UpdateClosureContour(positionEvent->GetPositionInWorld()); + m_CurrentRestrictedArea->AddVertex(positionEvent->GetPositionInWorld()); + + assert(positionEvent->GetSender()->GetRenderWindow()); + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } -void mitk::EditableContourTool::FinishTool() +void mitk::EditableContourTool::OnEndDrawing(StateMachineAction*, InteractionEvent* interactionEvent) { + auto* positionEvent = dynamic_cast(interactionEvent); + if (!positionEvent) + return; + + if (m_CurrentRestrictedArea->GetNumberOfVertices() > 1) + { + auto restrictedArea = m_CurrentRestrictedArea->Clone(); + m_RestrictedAreas.push_back(restrictedArea); + } + m_CurrentRestrictedArea = this->CreateNewContour(); + m_PreviewContourNode->SetVisibility(true); + m_Contour->SetControlVertexAt(m_Contour->GetNumberOfVertices() - 1); + + 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 numberOfTimesteps = static_cast(m_Contour->GetTimeSteps()); for (int i = 0; i <= numberOfTimesteps; ++i) m_Contour->Close(i); - this->GetToolManager()->GetDataStorage()->Remove(m_PreviewContourNode); - - m_PreviewContourNode = nullptr; - m_PreviewContour = nullptr; + this->ReleaseHelperObjects(false); } -void mitk::EditableContourTool::ReleaseHelperObjects() +void mitk::EditableContourTool::ReleaseHelperObjects(bool includeWorkingContour) { - this->RemoveHelperObjects(); + this->RemoveHelperObjectsFromDataStorage(includeWorkingContour); - m_EditingContours.clear(); - m_WorkingContours.clear(); - m_RestrictedAreas.clear(); + if (includeWorkingContour) + { + m_ContourNode = nullptr; + m_Contour = nullptr; - m_EditingContourNode = nullptr; - m_EditingContour = nullptr; + m_CurrentRestrictedArea = nullptr; + m_RestrictedAreas.clear(); + } m_PreviewContourNode = nullptr; m_PreviewContour = nullptr; m_ClosureContourNode = nullptr; m_ClosureContour = nullptr; - - m_ContourNode = nullptr; - m_Contour = nullptr; - - m_CurrentRestrictedArea = nullptr; } -void mitk::EditableContourTool::RemoveHelperObjects() +void mitk::EditableContourTool::RemoveHelperObjectsFromDataStorage(bool includeWorkingContour) { auto dataStorage = this->GetToolManager()->GetDataStorage(); if (nullptr == dataStorage) return; - for (const auto &editingContour : m_EditingContours) - dataStorage->Remove(editingContour.first); - - for (const auto &workingContour : m_WorkingContours) - dataStorage->Remove(workingContour.first); - - if (m_EditingContourNode.IsNotNull()) - dataStorage->Remove(m_EditingContourNode); + 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); - if (m_ContourNode.IsNotNull()) - dataStorage->Remove(m_ContourNode); - 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); m_ClosureContour->Update(); } if (m_ClosureContour->GetNumberOfVertices() == 2) { m_ClosureContour->RemoveVertexAt(0); } m_ClosureContour->AddVertexAtFront(endpoint); } + +void mitk::EditableContourTool::EnableContourInteraction(bool on) +{ + for (const auto& interactor : m_ContourInteractors) + interactor->EnableInteraction(on); +} + +void mitk::EditableContourTool::ReleaseInteractors() +{ + this->EnableContourInteraction(false); + m_ContourInteractors.clear(); +} diff --git a/Modules/Segmentation/Interactions/mitkEditableContourTool.h b/Modules/Segmentation/Interactions/mitkEditableContourTool.h index bc55161e76..e6e182c1f6 100644 --- a/Modules/Segmentation/Interactions/mitkEditableContourTool.h +++ b/Modules/Segmentation/Interactions/mitkEditableContourTool.h @@ -1,101 +1,102 @@ /*============================================================================ 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 all current contours to binary segmentations. - virtual void ConfirmSegmentation(); + /// \brief Convert current contour to binary segmentations. + virtual void ConfirmSegmentation(bool resetStatMachine = true); /// \brief Delete all current contours. virtual void ClearSegmentation(); 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) = 0; + 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) = 0; + virtual void OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent); - virtual void OnEndDrawing(StateMachineAction *, InteractionEvent *interactionEvent) = 0; + virtual void OnEndDrawing(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Computation of the preview contour. - virtual void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) = 0; + virtual void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Finish EditableContour tool. virtual void OnFinish(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Finish contour interaction. - virtual void FinishTool(); + virtual void FinishTool() = 0; - virtual void EnableContourInteraction(bool on) = 0; + void EnableContourInteraction(bool on); - virtual void ReleaseInteractors() = 0; + void ReleaseInteractors(); - virtual void ReleaseHelperObjects(); + virtual void ReleaseHelperObjects(bool includeWorkingContour = true); - virtual void RemoveHelperObjects(); + 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::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_EditingContour; - mitk::DataNode::Pointer m_EditingContourNode; - mitk::ContourModel::Pointer m_CurrentRestrictedArea; std::vector m_RestrictedAreas; /** Slice of the reference data the tool is currently actively working on to define contours.*/ mitk::Image::Pointer m_ReferenceDataSlice; - std::vector> m_WorkingContours; - std::vector> m_EditingContours; + PlaneGeometry::ConstPointer m_PlaneGeometry; - PlaneGeometry::ConstPointer m_PlaneGeometry; + std::vector m_ContourInteractors; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index e7d7819551..8cb29f1e8a 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,456 +1,262 @@ /*============================================================================ 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_SnapClosureContour(false), m_CreateAndUseDynamicCosts(false) + : EditableContourTool(), m_CreateAndUseDynamicCosts(false) { } mitk::LiveWireTool2D::~LiveWireTool2D() { - this->ClearSegmentation(); } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { mitk::EditableContourTool::ConnectActionsAndFunctions(); CONNECT_FUNCTION("MovePoint", OnMouseMoveNoDynamicCosts); } -void mitk::LiveWireTool2D::ReleaseInteractors() -{ - this->EnableContourInteraction(false); - m_LiveWireInteractors.clear(); -} - 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 timeGeometry = m_Contour->GetTimeGeometry()->Clone(); m_PreviewContour = this->m_LiveWireFilter->GetOutput(); - m_PreviewContour->SetTimeGeometry(timeGeometry); // needed because the results of the filter are always from 0 ms - // to 1 ms and the filter also resets its outputs. - m_PreviewContourNode->SetData(this->m_PreviewContour); - - if (m_SnapClosureContour) - { - m_ClosureContour = this->m_LiveWireFilterClosure->GetOutput(); - } - m_ClosureContour->SetTimeGeometry(timeGeometry); // needed because the results of the filter are always from 0 ms - // to 1 ms and the filter also resets its outputs. - m_ClosureContourNode->SetData(this->m_ClosureContour); + // 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); + 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(); - m_LiveWireFilterClosure->SetInput(m_ReferenceDataSlice); - - m_LiveWireFilterClosure->Update(); - this->UpdateLiveWireContour(); RenderingManager::GetInstance()->RequestUpdateAll(); }; -void mitk::LiveWireTool2D::EnableContourInteraction(bool on) -{ - for (const auto &interactor : m_LiveWireInteractors) - interactor->EnableInteraction(on); -} - - -void mitk::LiveWireTool2D::SetSnapClosureContour(bool snap) -{ - m_SnapClosureContour = snap; - if (!m_PreviewContour || !m_Contour) - { - return; - } - - if (m_PreviewContour->GetNumberOfVertices() > 0) - { - UpdateClosureContour(m_PreviewContour->GetVertexAt(m_PreviewContour->GetNumberOfVertices() - 1)->Coordinates); - } - else if (m_Contour->GetNumberOfVertices() > 0) - { - UpdateClosureContour(m_Contour->GetVertexAt(m_Contour->GetNumberOfVertices() - 1)->Coordinates); - } - - this->UpdateLiveWireContour(); -} - -void mitk::LiveWireTool2D::OnInitContour(StateMachineAction *s, InteractionEvent *interactionEvent) +mitk::Point3D mitk::LiveWireTool2D::PrepareInitContour(const mitk::Point3D& clickedPoint) { - mitk::EditableContourTool::OnInitContour(s, interactionEvent); - - auto positionEvent = dynamic_cast(interactionEvent); - if (nullptr == positionEvent) - return; - - auto workingDataNode = this->GetWorkingDataNode(); - - if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) - { - this->ResetToStartState(); - return; - } - // Set current slice as input for ImageToLiveWireContourFilter m_LiveWireFilter = ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetUseCostFunction(true); m_LiveWireFilter->SetInput(m_ReferenceDataSlice); - m_LiveWireFilterClosure = ImageLiveWireContourModelFilter::New(); - m_LiveWireFilterClosure->SetUseCostFunction(true); - m_LiveWireFilterClosure->SetInput(m_ReferenceDataSlice); - - // Map click to pixel coordinates - auto click = positionEvent->GetPositionInWorld(); itk::Index<3> idx; - m_ReferenceDataSlice->GetGeometry()->WorldToIndex(click, 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); - click[0] = indexWithHighestGradient[0]; - click[1] = indexWithHighestGradient[1]; - click[2] = indexWithHighestGradient[2]; - m_ReferenceDataSlice->GetGeometry()->IndexToWorld(click, click); - - // Set initial start point - m_Contour->AddVertex(click, true); - m_LiveWireFilter->SetStartPoint(click); - m_LiveWireFilterClosure->SetEndPoint(click); - - if (!m_SnapClosureContour) - { - m_ClosureContour->AddVertex(click); - } + Point3D adaptedClick; + m_ReferenceDataSlice->GetGeometry()->IndexToWorld(indexWithHighestGradient, adaptedClick); m_CreateAndUseDynamicCosts = true; - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); + return adaptedClick; } -void mitk::LiveWireTool2D::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::LiveWireTool2D::FinalizePreviewContour(const Point3D& clickedPoint) { - // Complete LiveWire interaction for the last segment. Add current LiveWire contour to - // the finished contour and reset to start a new segment and computation. - - 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; - } - // Add repulsive points to avoid getting the same path again - std::for_each(m_PreviewContour->IteratorBegin(), m_PreviewContour->IteratorEnd(), [this](ContourElement::VertexType *vertex) { + 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); - this->m_LiveWireFilterClosure->AddRepulsivePoint(idx); }); - // Remove duplicate first vertex, it's already contained in m_Contour - m_PreviewContour->RemoveVertexAt(0); - - // Set last point as control point - m_PreviewContour->SetControlVertexAt(m_PreviewContour->GetNumberOfVertices() - 1); - - // Merge contours - m_Contour->Concatenate(m_PreviewContour); + EditableContourTool::FinalizePreviewContour(clickedPoint); +} - // Clear the LiveWire contour and reset the corresponding DataNode +void mitk::LiveWireTool2D::InitializePreviewContour(const Point3D& clickedPoint) +{ m_PreviewContour->Clear(); // Set new start point - m_LiveWireFilter->SetStartPoint(positionEvent->GetPositionInWorld()); - m_LiveWireFilterClosure->SetStartPoint(positionEvent->GetPositionInWorld()); + m_LiveWireFilter->SetStartPoint(clickedPoint); if (m_CreateAndUseDynamicCosts) { // Use dynamic cost map for next update m_LiveWireFilter->CreateDynamicCostMap(m_Contour); m_LiveWireFilter->SetUseDynamicCostMap(true); - - m_LiveWireFilterClosure->CreateDynamicCostMap(m_Contour); - m_LiveWireFilterClosure->SetUseDynamicCostMap(true); } - - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } -void mitk::LiveWireTool2D::OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent) -{ - auto *positionEvent = dynamic_cast(interactionEvent); - if (!positionEvent) - return; - - m_PreviewContour->AddVertex(positionEvent->GetPositionInWorld()); +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(); - UpdateClosureContour(positionEvent->GetPositionInWorld()); - - m_CurrentRestrictedArea->AddVertex(positionEvent->GetPositionInWorld()); - this->UpdateLiveWireContour(); - - assert(positionEvent->GetSender()->GetRenderWindow()); - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::LiveWireTool2D::OnEndDrawing(StateMachineAction *s, InteractionEvent *interactionEvent) -{ - if (m_CurrentRestrictedArea->GetNumberOfVertices() > 1) - { - auto restrictedArea = m_CurrentRestrictedArea->Clone(); - m_RestrictedAreas.push_back(restrictedArea); - OnAddPoint(s, interactionEvent); - } - m_CurrentRestrictedArea = this->CreateNewContour(); -} - -void mitk::LiveWireTool2D::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) -{ - // Compute LiveWire segment from last control point to current mouse position - - 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; - } - - m_LiveWireFilter->SetEndPoint(positionEvent->GetPositionInWorld()); - m_LiveWireFilter->Update(); - - UpdateClosureContour(positionEvent->GetPositionInWorld()); - - this->UpdateLiveWireContour(); - - RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::LiveWireTool2D::UpdateClosureContour(mitk::Point3D endpoint) -{ - if (m_SnapClosureContour) - { - m_LiveWireFilterClosure->SetStartPoint(endpoint); - m_LiveWireFilterClosure->Update(); - } - else - { - mitk::EditableContourTool::UpdateClosureContour(endpoint); - } } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent) { m_LiveWireFilter->SetUseDynamicCostMap(false); - m_LiveWireFilterClosure->SetUseDynamicCostMap(false); this->OnMouseMoved(nullptr, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); - m_LiveWireFilterClosure->SetUseDynamicCostMap(true); -} - -bool mitk::LiveWireTool2D::OnCheckPoint(const InteractionEvent *interactionEvent) -{ - // Check double click on first control point to finish the LiveWire tool - - auto positionEvent = dynamic_cast(interactionEvent); - - if (nullptr == positionEvent) - return false; - - mitk::Point3D click = positionEvent->GetPositionInWorld(); - mitk::Point3D first = this->m_Contour->GetVertexAt(0)->Coordinates; - - return first.EuclideanDistanceTo(click) < 4.5; -} - -void mitk::LiveWireTool2D::OnFinish(StateMachineAction *s, InteractionEvent *interactionEvent) -{ - // Finish LiveWire tool interaction - if (!OnCheckPoint(interactionEvent)) - { - m_Contour->Concatenate(m_ClosureContour); - } - - mitk::EditableContourTool::OnFinish(s, interactionEvent); - - m_LiveWireFilter->SetUseDynamicCostMap(false); - m_LiveWireFilterClosure->SetUseDynamicCostMap(false); - - this->FinishTool(); } void mitk::LiveWireTool2D::FinishTool() { - mitk::EditableContourTool::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->SetEditingContourModelNode(this->m_EditingContourNode); - m_ContourInteractor->SetRestrictedAreas(this->m_RestrictedAreas); + //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->SetEditingContourModelNode(this->m_EditingContourNode); + //m_ContourInteractor->SetRestrictedAreas(this->m_RestrictedAreas); - m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); + //m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); - this->m_LiveWireInteractors.push_back(m_ContourInteractor); + //this->m_ContourInteractors.push_back(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/Interactions/mitkLiveWireTool2D.h b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h index 8638f02a19..a598e8c3bb 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h @@ -1,117 +1,90 @@ /*============================================================================ 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 mitkLiveWireTool2D_h #define mitkLiveWireTool2D_h #include #include namespace mitk { /** \brief A 2D segmentation tool based on a LiveWire approach. The contour between the last point and the current mouse position is computed by searching the shortest path according to specific features of the image. The contour thus tends to snap to the boundary of objects. The tool always assumes that unconfirmed contours are always defined for the current time point. So the time step in which the contours will be stored as segmentations will be determined when the contours got confirmed. Then they will be transfered to the slices of the currently selected time step. Changing the time point/time step while tool is active will updated the working slice the live wire filter. So the behavior of the active live wire contour is always WYSIWYG (What you see is what you get). \sa SegTool2D \sa ImageLiveWireContourModelFilter \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT LiveWireTool2D : public EditableContourTool { public: - mitkClassMacro(LiveWireTool2D, SegTool2D); + mitkClassMacro(LiveWireTool2D, EditableContourTool); itkFactorylessNewMacro(Self); us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; const char **GetXPM() const override; - void SetSnapClosureContour(bool snap); - protected: LiveWireTool2D(); ~LiveWireTool2D() override; void ConnectActionsAndFunctions() override; void UpdateLiveWireContour(); void OnTimePointChanged() override; - private: - /// \brief Initialize tool. - void OnInitContour(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Add a control point and finish current segment. - void OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Draw a contour according to the mouse movement when mouse button is pressed and mouse is moved. - void OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent) override; - - void OnEndDrawing(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Actual LiveWire computation. - void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Check double click on first control point to finish the LiveWire tool. - bool OnCheckPoint(const InteractionEvent *interactionEvent); + mitk::Point3D PrepareInitContour(const mitk::Point3D& clickedPoint) override; + virtual void FinalizePreviewContour(const Point3D& clickedPoint) override; + virtual void InitializePreviewContour(const Point3D& clickedPoint) override; + virtual void UpdatePreviewContour(const Point3D& clickedPoint) override; - /// \brief Finish LiveWire tool. - void OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) override; + private: /// \brief Don't use dynamic cost map for LiveWire calculation. void OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Finish contour interaction. void FinishTool() override; - void EnableContourInteraction(bool on) override; - - void ReleaseInteractors() override; - template void FindHighestGradientMagnitudeByITK(itk::Image *inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex); - void UpdateClosureContour(mitk::Point3D endpoint) override; - - bool m_SnapClosureContour; - mitk::ContourModelLiveWireInteractor::Pointer m_ContourInteractor; mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter; - mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilterClosure; bool m_CreateAndUseDynamicCosts; - - std::vector m_LiveWireInteractors; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkNewAddTool2D.cpp b/Modules/Segmentation/Interactions/mitkNewAddTool2D.cpp index 5273bdfc54..591879018f 100644 --- a/Modules/Segmentation/Interactions/mitkNewAddTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkNewAddTool2D.cpp @@ -1,205 +1,71 @@ /*============================================================================ 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 namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, NewAddTool2D, "New Add tool"); } mitk::NewAddTool2D::NewAddTool2D() : EditableContourTool() { } mitk::NewAddTool2D::~NewAddTool2D() {} void mitk::NewAddTool2D::ConnectActionsAndFunctions() { mitk::EditableContourTool::ConnectActionsAndFunctions(); CONNECT_FUNCTION("MovePoint", OnMouseMoved); } -void mitk::NewAddTool2D::ReleaseInteractors() -{ - this->EnableContourInteraction(false); - m_ContourInteractors.clear(); -} - const char **mitk::NewAddTool2D::GetXPM() const { return nullptr; } us::ModuleResource mitk::NewAddTool2D::GetIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("NewAdd_48x48.png"); } us::ModuleResource mitk::NewAddTool2D::GetCursorIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("NewAdd_Cursor_32x32.png"); } const char *mitk::NewAddTool2D::GetName() const { return "New Add"; } -void mitk::NewAddTool2D::EnableContourInteraction(bool on) -{ - for (const auto &interactor : m_ContourInteractors) - interactor->EnableInteraction(on); -} - -void mitk::NewAddTool2D::OnInitContour(StateMachineAction *s, InteractionEvent *interactionEvent) -{ - mitk::EditableContourTool::OnInitContour(s, interactionEvent); - - auto positionEvent = dynamic_cast(interactionEvent); - if (nullptr == positionEvent) - return; - - auto workingDataNode = this->GetWorkingDataNode(); - - if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) - { - this->ResetToStartState(); - return; - } - - // Map click to pixel coordinates - auto click = positionEvent->GetPositionInWorld(); - - // Set initial start point - m_Contour->AddVertex(click, true); - m_PreviewContour->AddVertex(click, false); - m_ClosureContour->AddVertex(click); - - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::NewAddTool2D::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; - } - - m_Contour->AddVertex(positionEvent->GetPositionInWorld(), true); - - std::for_each(m_PreviewContour->IteratorBegin(), m_PreviewContour->IteratorEnd(), [this](ContourElement::VertexType* vertex) - { - m_PreviewContour->RemoveVertex(vertex); - }); - - m_PreviewContour->AddVertex(positionEvent->GetPositionInWorld()); - m_PreviewContour->Update(); - - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::NewAddTool2D::OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent) -{ - auto *positionEvent = dynamic_cast(interactionEvent); - if (!positionEvent) - return; - - m_PreviewContour->AddVertex(positionEvent->GetPositionInWorld(), false); - UpdateClosureContour(positionEvent->GetPositionInWorld()); - m_CurrentRestrictedArea->AddVertex(positionEvent->GetPositionInWorld()); - - assert(positionEvent->GetSender()->GetRenderWindow()); - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::NewAddTool2D::OnEndDrawing(StateMachineAction *, InteractionEvent *) -{ - if (m_CurrentRestrictedArea->GetNumberOfVertices() > 1) - { - auto restrictedArea = m_CurrentRestrictedArea->Clone(); - m_RestrictedAreas.push_back(restrictedArea); - // Remove duplicate first vertex, it's already contained in m_Contour - m_PreviewContour->RemoveVertexAt(0); - // Set last point as control point - m_PreviewContour->SetControlVertexAt(m_PreviewContour->GetNumberOfVertices() - 1); - // Merge contours - m_Contour->Concatenate(m_PreviewContour); - std::for_each(m_PreviewContour->IteratorBegin(), m_PreviewContour->IteratorEnd(), [this](ContourElement::VertexType *vertex) - { - m_PreviewContour->RemoveVertex(vertex); - }); - } - m_CurrentRestrictedArea = this->CreateNewContour(); -} - -void mitk::NewAddTool2D::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; - } - - if (m_PreviewContour->GetNumberOfVertices() > 1) - { - m_PreviewContour->RemoveVertexAt(m_PreviewContour->GetNumberOfVertices() - 1); - } - m_PreviewContour->AddVertex(positionEvent->GetPositionInWorld()); - m_PreviewContour->Update(); - - this->UpdateClosureContour(positionEvent->GetPositionInWorld()); - - RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::NewAddTool2D::OnFinish(StateMachineAction *s, InteractionEvent *interactionEvent) -{ - mitk::EditableContourTool::OnFinish(s, interactionEvent); - this->FinishTool(); -} - void mitk::NewAddTool2D::FinishTool() { - mitk::EditableContourTool::FinishTool(); - - m_ContourInteractor = mitk::ContourModelInteractor::New(); - m_ContourInteractor->SetDataNode(m_ContourNode); - m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); - m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); - m_ContourInteractor->SetRestrictedAreas(this->m_RestrictedAreas); + //m_ContourInteractor = mitk::ContourModelInteractor::New(); + //m_ContourInteractor->SetDataNode(m_ContourNode); + //m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); + //m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); + //m_ContourInteractor->SetRestrictedAreas(this->m_RestrictedAreas); - m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); + //m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); - this->m_ContourInteractors.push_back(m_ContourInteractor); + //this->m_ContourInteractors.push_back(m_ContourInteractor); } diff --git a/Modules/Segmentation/Interactions/mitkNewAddTool2D.h b/Modules/Segmentation/Interactions/mitkNewAddTool2D.h index d0a453d84b..662300b1a2 100644 --- a/Modules/Segmentation/Interactions/mitkNewAddTool2D.h +++ b/Modules/Segmentation/Interactions/mitkNewAddTool2D.h @@ -1,88 +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 mitkNewAddTool2D_h #define mitkNewAddTool2D_h #include "mitkEditableContourTool.h" #include "mitkContourTool.h" #include "mitkContourModelInteractor.h" namespace mitk { /** \brief A 2D segmentation tool to draw polygon structures. The contour between the last point and the current mouse position is computed by searching the shortest path according to specific features of the image. The contour thus tends to snap to the boundary of objects. The tool always assumes that unconfirmed contours are always defined for the current time point. So the time step in which the contours will be stored as segmentations will be determined when the contours got confirmed. Then they will be transfered to the slices of the currently selected time step. \sa SegTool2D \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT NewAddTool2D : public EditableContourTool { public: mitkClassMacro(NewAddTool2D, SegTool2D); itkFactorylessNewMacro(Self); us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; const char **GetXPM() const override; protected: NewAddTool2D(); ~NewAddTool2D() override; void ConnectActionsAndFunctions() override; - - private: - /// \brief Initialize tool. - void OnInitContour(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Add a control point and finish current segment. - void OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Draw a contour according to the mouse movement when mouse button is pressed and mouse is moved. - void OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent) override; - - void OnEndDrawing(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Actual contour computation. - void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Finish contour tool. - void OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) override; - - /// \brief Finish contour interaction. void FinishTool() override; - void EnableContourInteraction(bool on) override; - - void ReleaseInteractors() override; - + private: mitk::ContourModelInteractor::Pointer m_ContourInteractor; - std::vector m_ContourInteractors; }; } #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp index 19887dc125..dbb8d010a3 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp @@ -1,66 +1,66 @@ /*============================================================================ 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 "QmitkLiveWireTool2DGUI.h" #include "mitkBaseRenderer.h" #include "mitkStepper.h" #include #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkLiveWireTool2DGUI, "") QmitkLiveWireTool2DGUI::QmitkLiveWireTool2DGUI() : QmitkToolGUI() { m_Controls.setupUi(this); m_Controls.m_Information->hide(); connect(m_Controls.m_ConfirmButton, SIGNAL(clicked()), this, SLOT(OnConfirmSegmentation())); connect(m_Controls.m_ClearButton, SIGNAL(clicked()), this, SLOT(OnClearSegmentation())); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); connect(m_Controls.m_SnapClosureCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnSnapClosureContour(bool))); connect(m_Controls.m_InformationCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnShowInformation(bool))); } QmitkLiveWireTool2DGUI::~QmitkLiveWireTool2DGUI() { } void QmitkLiveWireTool2DGUI::OnNewToolAssociated(mitk::Tool *tool) { m_LiveWireTool = dynamic_cast(tool); } void QmitkLiveWireTool2DGUI::OnConfirmSegmentation() { if (m_LiveWireTool.IsNotNull()) m_LiveWireTool->ConfirmSegmentation(); } void QmitkLiveWireTool2DGUI::OnClearSegmentation() { if (m_LiveWireTool.IsNotNull()) m_LiveWireTool->ClearSegmentation(); } void QmitkLiveWireTool2DGUI::OnSnapClosureContour(bool snap) { - m_LiveWireTool->SetSnapClosureContour(snap); + } void QmitkLiveWireTool2DGUI::OnShowInformation(bool on) { m_Controls.m_Information->setVisible(on); }