diff --git a/Modules/Segmentation/Controllers/mitkToolManager.cpp b/Modules/Segmentation/Controllers/mitkToolManager.cpp index 852d3b52f1..6e2fbd852c 100644 --- a/Modules/Segmentation/Controllers/mitkToolManager.cpp +++ b/Modules/Segmentation/Controllers/mitkToolManager.cpp @@ -1,523 +1,589 @@ /*============================================================================ 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 "mitkToolManager.h" #include "mitkToolManagerProvider.h" #include "mitkCoreObjectFactory.h" #include #include #include #include "mitkInteractionEventObserver.h" #include "mitkSegTool2D.h" +#include "mitkRenderingManager.h" +#include "mitkSliceNavigationController.h" #include "usGetModuleContext.h" #include "usModuleContext.h" mitk::ToolManager::ToolManager(DataStorage *storage) : m_ActiveTool(nullptr), m_ActiveToolID(-1), m_RegisteredClients(0), m_DataStorage(storage) { CoreObjectFactory::GetInstance(); // to make sure a CoreObjectFactory was instantiated (and in turn, possible tools // are registered) - bug 1029 this->InitializeTools(); } +void mitk::ToolManager::EnsureTimeObservation() +{ + if (nullptr != mitk::RenderingManager::GetInstance() && nullptr != mitk::RenderingManager::GetInstance()->GetTimeNavigationController()) + { + auto timeController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); + + m_LastTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + + auto currentTimeController = m_CurrentTimeNavigationController.Lock(); + + if (timeController != currentTimeController) + { + if (currentTimeController.IsNotNull()) + { + currentTimeController->RemoveObserver(m_TimePointObserverTag); + } + + itk::MemberCommand::Pointer command = itk::MemberCommand::New(); + command->SetCallbackFunction(this, &ToolManager::OnTimeChanged); + command->SetCallbackFunction(this, &ToolManager::OnTimeChangedConst); + m_CurrentTimeNavigationController = timeController; + m_TimePointObserverTag = timeController->AddObserver(SliceNavigationController::GeometryTimeEvent(nullptr,0), command); + } + } +} + +void mitk::ToolManager::StopTimeObservation() +{ + auto currentTimeController = m_CurrentTimeNavigationController.Lock(); + + if (currentTimeController.IsNotNull()) + { + currentTimeController->RemoveObserver(m_TimePointObserverTag); + m_CurrentTimeNavigationController = nullptr; + m_TimePointObserverTag = 0; + } +} + mitk::ToolManager::~ToolManager() { for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) (*dataIter)->RemoveObserver(m_WorkingDataObserverTags[(*dataIter)]); if (this->GetDataStorage() != nullptr) this->GetDataStorage()->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &ToolManager::OnNodeRemoved)); if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); m_ActiveTool = nullptr; m_ActiveToolID = -1; // no tool active ActiveToolChanged.Send(); } for (auto observerTagMapIter = m_ReferenceDataObserverTags.begin(); observerTagMapIter != m_ReferenceDataObserverTags.end(); ++observerTagMapIter) { observerTagMapIter->first->RemoveObserver(observerTagMapIter->second); } + this->StopTimeObservation(); } void mitk::ToolManager::InitializeTools() { // clear all previous tool pointers (tools may be still activated from another recently used plugin) if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); m_ActiveTool = nullptr; m_ActiveToolID = -1; // no tool active ActiveToolChanged.Send(); } m_Tools.clear(); // get a list of all known mitk::Tools std::list thingsThatClaimToBeATool = itk::ObjectFactoryBase::CreateAllInstance("mitkTool"); // remember these tools for (auto iter = thingsThatClaimToBeATool.begin(); iter != thingsThatClaimToBeATool.end(); ++iter) { if (auto *tool = dynamic_cast(iter->GetPointer())) { tool->InitializeStateMachine(); tool->SetToolManager(this); // important to call right after instantiation tool->ErrorMessage += MessageDelegate1(this, &ToolManager::OnToolErrorMessage); tool->GeneralMessage += MessageDelegate1(this, &ToolManager::OnGeneralToolMessage); m_Tools.push_back(tool); } } } void mitk::ToolManager::OnToolErrorMessage(std::string s) { this->ToolErrorMessage(s); } void mitk::ToolManager::OnGeneralToolMessage(std::string s) { this->GeneralToolMessage(s); } const mitk::ToolManager::ToolVectorTypeConst mitk::ToolManager::GetTools() { ToolVectorTypeConst resultList; for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter) { resultList.push_back(iter->GetPointer()); } return resultList; } mitk::Tool *mitk::ToolManager::GetToolById(int id) { try { return m_Tools.at(id); } catch (const std::exception &) { return nullptr; } } bool mitk::ToolManager::ActivateTool(int id) { if (id != -1 && !this->GetToolById(id)->CanHandle(this->GetReferenceData(0)->GetData())) return false; if (this->GetDataStorage()) { this->GetDataStorage()->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &ToolManager::OnNodeRemoved)); } if (GetToolById(id) == m_ActiveTool) return true; // no change needed static int nextTool = -1; nextTool = id; static bool inActivateTool = false; if (inActivateTool) { return true; } inActivateTool = true; while (nextTool != m_ActiveToolID) { // Deactivate all other active tools to ensure a globally single active tool for (const auto& toolManager : ToolManagerProvider::GetInstance()->GetToolManagers()) { if (nullptr != toolManager.second->m_ActiveTool) { toolManager.second->m_ActiveTool->Deactivated(); toolManager.second->m_ActiveToolRegistration.Unregister(); // The active tool of *this* ToolManager is handled below this loop if (this != toolManager.second) { toolManager.second->m_ActiveTool = nullptr; toolManager.second->m_ActiveToolID = -1; toolManager.second->ActiveToolChanged.Send(); } } } m_ActiveTool = GetToolById(nextTool); m_ActiveToolID = m_ActiveTool ? nextTool : -1; // current ID if tool is valid, otherwise -1 ActiveToolChanged.Send(); if (m_ActiveTool) { + this->EnsureTimeObservation(); if (m_RegisteredClients > 0) { m_ActiveTool->Activated(); m_ActiveToolRegistration = us::GetModuleContext()->RegisterService(m_ActiveTool, us::ServiceProperties()); } } } inActivateTool = false; return (m_ActiveTool != nullptr); } void mitk::ToolManager::SetReferenceData(DataVectorType data) { if (data != m_ReferenceData) { // remove observers from old nodes for (auto dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter) { auto searchIter = m_ReferenceDataObserverTags.find(*dataIter); if (searchIter != m_ReferenceDataObserverTags.end()) { (*dataIter)->RemoveObserver(searchIter->second); } } m_ReferenceData = data; // TODO tell active tool? // attach new observers m_ReferenceDataObserverTags.clear(); for (auto dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter) { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheReferenceDataDeleted); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheReferenceDataDeletedConst); m_ReferenceDataObserverTags.insert( std::pair((*dataIter), (*dataIter)->AddObserver(itk::DeleteEvent(), command))); } ReferenceDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheReferenceDataDeletedConst(const itk::Object *caller, const itk::EventObject &e) { OnOneOfTheReferenceDataDeleted(const_cast(caller), e); } void mitk::ToolManager::OnOneOfTheReferenceDataDeleted(itk::Object *caller, const itk::EventObject &itkNotUsed(e)) { DataVectorType v; for (auto dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter) { if ((void *)(*dataIter) != (void *)caller) { v.push_back(*dataIter); } else { m_ReferenceDataObserverTags.erase(*dataIter); // no tag to remove anymore } } this->SetReferenceData(v); } void mitk::ToolManager::SetReferenceData(DataNode *data) { DataVectorType v; if (data) { v.push_back(data); } SetReferenceData(v); } void mitk::ToolManager::SetWorkingData(DataVectorType data) { if (data != m_WorkingData) { // remove observers from old nodes for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) { auto searchIter = m_WorkingDataObserverTags.find(*dataIter); if (searchIter != m_WorkingDataObserverTags.end()) { (*dataIter)->RemoveObserver(searchIter->second); } } m_WorkingData = data; // TODO tell active tool? // Quick workaround for bug #16598 if (m_WorkingData.empty()) this->ActivateTool(-1); // workaround end // attach new observers m_WorkingDataObserverTags.clear(); for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheWorkingDataDeleted); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheWorkingDataDeletedConst); m_WorkingDataObserverTags.insert( std::pair((*dataIter), (*dataIter)->AddObserver(itk::DeleteEvent(), command))); } WorkingDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheWorkingDataDeletedConst(const itk::Object *caller, const itk::EventObject &e) { OnOneOfTheWorkingDataDeleted(const_cast(caller), e); } void mitk::ToolManager::OnOneOfTheWorkingDataDeleted(itk::Object *caller, const itk::EventObject &itkNotUsed(e)) { DataVectorType v; for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) { if ((void *)(*dataIter) != (void *)caller) { v.push_back(*dataIter); } else { m_WorkingDataObserverTags.erase(*dataIter); // no tag to remove anymore } } this->SetWorkingData(v); } void mitk::ToolManager::SetWorkingData(DataNode *data) { DataVectorType v; if (data) // don't allow for nullptr nodes { v.push_back(data); } SetWorkingData(v); } void mitk::ToolManager::SetRoiData(DataVectorType data) { if (data != m_RoiData) { // remove observers from old nodes for (auto dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter) { auto searchIter = m_RoiDataObserverTags.find(*dataIter); if (searchIter != m_RoiDataObserverTags.end()) { (*dataIter)->RemoveObserver(searchIter->second); } } m_RoiData = data; // TODO tell active tool? // attach new observers m_RoiDataObserverTags.clear(); for (auto dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter) { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheRoiDataDeleted); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheRoiDataDeletedConst); m_RoiDataObserverTags.insert( std::pair((*dataIter), (*dataIter)->AddObserver(itk::DeleteEvent(), command))); } RoiDataChanged.Send(); } } void mitk::ToolManager::SetRoiData(DataNode *data) { DataVectorType v; if (data) { v.push_back(data); } this->SetRoiData(v); } void mitk::ToolManager::OnOneOfTheRoiDataDeletedConst(const itk::Object *caller, const itk::EventObject &e) { OnOneOfTheRoiDataDeleted(const_cast(caller), e); } void mitk::ToolManager::OnOneOfTheRoiDataDeleted(itk::Object *caller, const itk::EventObject &itkNotUsed(e)) { DataVectorType v; for (auto dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter) { if ((void *)(*dataIter) != (void *)caller) { v.push_back(*dataIter); } else { m_RoiDataObserverTags.erase(*dataIter); // no tag to remove anymore } } this->SetRoiData(v); } mitk::ToolManager::DataVectorType mitk::ToolManager::GetReferenceData() { return m_ReferenceData; } mitk::DataNode *mitk::ToolManager::GetReferenceData(int idx) { try { return m_ReferenceData.at(idx); } catch (const std::exception &) { return nullptr; } } mitk::ToolManager::DataVectorType mitk::ToolManager::GetWorkingData() { return m_WorkingData; } mitk::ToolManager::DataVectorType mitk::ToolManager::GetRoiData() { return m_RoiData; } mitk::DataNode *mitk::ToolManager::GetRoiData(int idx) { try { return m_RoiData.at(idx); } catch (const std::exception &) { return nullptr; } } mitk::DataStorage *mitk::ToolManager::GetDataStorage() { if (!m_DataStorage.IsExpired()) { return m_DataStorage.Lock(); } else { return nullptr; } } void mitk::ToolManager::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; } mitk::DataNode *mitk::ToolManager::GetWorkingData(unsigned int idx) { if (m_WorkingData.empty()) return nullptr; if (m_WorkingData.size() > idx) return m_WorkingData[idx]; return nullptr; } int mitk::ToolManager::GetActiveToolID() { return m_ActiveToolID; } mitk::Tool *mitk::ToolManager::GetActiveTool() { return m_ActiveTool; } void mitk::ToolManager::RegisterClient() { if (m_RegisteredClients < 1) { if (m_ActiveTool) { m_ActiveTool->Activated(); m_ActiveToolRegistration = us::GetModuleContext()->RegisterService(m_ActiveTool, us::ServiceProperties()); } } ++m_RegisteredClients; } void mitk::ToolManager::UnregisterClient() { if (m_RegisteredClients < 1) return; --m_RegisteredClients; if (m_RegisteredClients < 1) { if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); } } } int mitk::ToolManager::GetToolID(const Tool *tool) { int id(0); for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id) { if (tool == iter->GetPointer()) { return id; } } return -1; } void mitk::ToolManager::OnNodeRemoved(const mitk::DataNode *node) { // check all storage vectors OnOneOfTheReferenceDataDeleted(const_cast(node), itk::DeleteEvent()); OnOneOfTheRoiDataDeleted(const_cast(node), itk::DeleteEvent()); OnOneOfTheWorkingDataDeleted(const_cast(node), itk::DeleteEvent()); } + +void mitk::ToolManager::OnTimeChanged(itk::Object* caller, const itk::EventObject& e) +{ + this->OnTimeChangedConst(caller, e); +} + +void mitk::ToolManager::OnTimeChangedConst(const itk::Object* caller, const itk::EventObject& /*e*/) +{ + auto currentController = m_CurrentTimeNavigationController.Lock(); + if (caller == currentController) + { + const auto currentTimePoint = currentController->GetSelectedTimePoint(); + if (currentTimePoint != m_LastTimePoint) + { + m_LastTimePoint = currentTimePoint; + SelectedTimePointChanged.Send(); + } + } +} + +mitk::TimePointType mitk::ToolManager::GetCurrentTimePoint() const +{ + return m_LastTimePoint; +} diff --git a/Modules/Segmentation/Controllers/mitkToolManager.h b/Modules/Segmentation/Controllers/mitkToolManager.h index 6edbac0e7c..4cdfbf263a 100644 --- a/Modules/Segmentation/Controllers/mitkToolManager.h +++ b/Modules/Segmentation/Controllers/mitkToolManager.h @@ -1,282 +1,302 @@ /*============================================================================ 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 mitkToolManager_h_Included #define mitkToolManager_h_Included #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkTool.h" #include "mitkWeakPointer.h" #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include namespace mitk { class Image; class PlaneGeometry; /** \brief Manages and coordinates instances of mitk::Tool. \sa QmitkToolSelectionBox \sa Tool \sa QmitkSegmentationView \ingroup Interaction \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkSegmentationView: \ref QmitkSegmentationTechnicalPage This class creates and manages several instances of mitk::Tool. \li ToolManager creates instances of mitk::Tool by asking the itk::ObjectFactory to list all known implementations of mitk::Tool. As a result, one has to implement both a subclass of mitk::Tool and a matching subclass of itk::ObjectFactoryBase that is registered to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitiation of one-class-one-factory is due to the implementation of itk::ObjectFactory). In MITK, the right place to register the factories to itk::ObjectFactory is the mitk::QMCoreObjectFactory or mitk::SBCoreObjectFactory. \li ToolManager knows a set of "reference" DataNodes and a set of "working" DataNodes. The first application are segmentation tools, where the reference is the original image and the working data the (kind of) binary segmentation. However, ToolManager is implemented more generally, so that there could be other tools that work, e.g., with surfaces. \li There is a set of events that are sent by ToolManager. At the moment these are TODO update documentation: - mitk::ToolReferenceDataChangedEvent whenever somebody calls SetReferenceData. Most of the time this actually means that the data has changed, but there might be cases where the same data is passed to SetReferenceData a second time, so don't rely on the assumption that something actually changed. - mitk::ToolSelectedEvent is sent when a (truly) different tool was activated. In reaction to this event you can ask for the active Tool using GetActiveTool or GetActiveToolID (where nullptr or -1 indicate that NO tool is active at the moment). Design descisions: \li Not a singleton, because there could be two functionalities using tools, each one with different reference/working data. $Author$ */ class MITKSEGMENTATION_EXPORT ToolManager : public itk::Object { public: typedef std::vector ToolVectorType; typedef std::vector ToolVectorTypeConst; typedef std::vector DataVectorType; // has to be observed for delete events! typedef std::map NodeTagMapType; Message<> NodePropertiesChanged; Message<> NewNodesGenerated; Message1 NewNodeObjectsGenerated; Message<> ActiveToolChanged; Message<> ReferenceDataChanged; Message<> WorkingDataChanged; Message<> RoiDataChanged; + Message<> SelectedTimePointChanged; Message1 ToolErrorMessage; Message1 GeneralToolMessage; mitkClassMacroItkParent(ToolManager, itk::Object); mitkNewMacro1Param(ToolManager, DataStorage *); /** \brief Gives you a list of all tools. This is const on purpose. */ const ToolVectorTypeConst GetTools(); int GetToolID(const Tool *tool); - /* + /** \param id The tool of interest. Counting starts with 0. */ Tool *GetToolById(int id); /** \param id The tool to activate. Provide -1 for disabling any tools. Counting starts with 0. Registeres a listner for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved). */ bool ActivateTool(int id); template int GetToolIdByToolType() { int id = 0; for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id) { if (dynamic_cast(iter->GetPointer())) { return id; } } return -1; } /** \return -1 for "No tool is active" */ int GetActiveToolID(); /** \return nullptr for "No tool is active" */ Tool *GetActiveTool(); - /* + /** \brief Set a list of data/images as reference objects. */ void SetReferenceData(DataVectorType); - /* + /** \brief Set single data item/image as reference object. */ void SetReferenceData(DataNode *); - /* + /** \brief Set a list of data/images as working objects. */ void SetWorkingData(DataVectorType); - /* + /** \brief Set single data item/image as working object. */ void SetWorkingData(DataNode *); - /* + /** \brief Set a list of data/images as roi objects. */ void SetRoiData(DataVectorType); - /* + /** \brief Set a single data item/image as roi object. */ void SetRoiData(DataNode *); - /* + /** \brief Get the list of reference data. */ DataVectorType GetReferenceData(); - /* + /** \brief Get the current reference data. \warning If there is a list of items, this method will only return the first list item. */ DataNode *GetReferenceData(int); - /* + /** \brief Get the list of working data. */ DataVectorType GetWorkingData(); - /* + /** \brief Get the current working data. \warning If there is a list of items, this method will only return the first list item. */ DataNode *GetWorkingData(unsigned int); - /* + /** \brief Get the current roi data */ DataVectorType GetRoiData(); - /* + /** \brief Get the roi data at position idx */ DataNode *GetRoiData(int idx); DataStorage *GetDataStorage(); void SetDataStorage(DataStorage &storage); - /* + /** Get the current selected time point of the RenderManager + */ + TimePointType GetCurrentTimePoint() const; + + /** \brief Tell that someone is using tools. GUI elements should call this when they become active. This method increases an internal "client count". */ void RegisterClient(); - /* + /** \brief Tell that someone is NOT using tools. GUI elements should call this when they become active. This method increases an internal "client count". */ void UnregisterClient(); /** \brief Initialize all classes derived from mitk::Tool by itkObjectFactoy */ void InitializeTools(); void OnOneOfTheReferenceDataDeletedConst(const itk::Object *caller, const itk::EventObject &e); void OnOneOfTheReferenceDataDeleted(itk::Object *caller, const itk::EventObject &e); void OnOneOfTheWorkingDataDeletedConst(const itk::Object *caller, const itk::EventObject &e); void OnOneOfTheWorkingDataDeleted(itk::Object *caller, const itk::EventObject &e); void OnOneOfTheRoiDataDeletedConst(const itk::Object *caller, const itk::EventObject &e); void OnOneOfTheRoiDataDeleted(itk::Object *caller, const itk::EventObject &e); - /* + /** \brief Connected to tool's messages This method just resends error messages coming from any of the tools. This way clients (GUIs) only have to observe one message. */ void OnToolErrorMessage(std::string s); void OnGeneralToolMessage(std::string s); protected: /** You may specify a list of tool "groups" that should be available for this ToolManager. Every Tool can report its group as a string. This constructor will try to find the tool's group inside the supplied string. If there is a match, the tool is accepted. Effectively, you can provide a human readable list like "default, lymphnodevolumetry, oldERISstuff". */ ToolManager(DataStorage *storage); // purposely hidden ~ToolManager() override; ToolVectorType m_Tools; Tool *m_ActiveTool; int m_ActiveToolID; us::ServiceRegistration m_ActiveToolRegistration; DataVectorType m_ReferenceData; NodeTagMapType m_ReferenceDataObserverTags; DataVectorType m_WorkingData; NodeTagMapType m_WorkingDataObserverTags; DataVectorType m_RoiData; NodeTagMapType m_RoiDataObserverTags; int m_RegisteredClients; WeakPointer m_DataStorage; /// \brief Callback for NodeRemove events void OnNodeRemoved(const mitk::DataNode *node); + + /** Callback for time changed events*/ + void OnTimeChangedConst(const itk::Object* caller, const itk::EventObject& e); + void OnTimeChanged(itk::Object* caller, const itk::EventObject& e); + + void EnsureTimeObservation(); + void StopTimeObservation(); + + private: + /** Time point of last detected change*/ + TimePointType m_LastTimePoint = 0; + /** Tag of the observer that listens to time changes*/ + unsigned long m_TimePointObserverTag = 0; + /** Pointer to the observed time stepper*/ + WeakPointer m_CurrentTimeNavigationController; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp index c360e1db47..0539cb7df1 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp @@ -1,98 +1,97 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkAutoSegmentationTool.h" #include "mitkImage.h" #include "mitkToolManager.h" #include mitk::AutoSegmentationTool::AutoSegmentationTool() : Tool("dummy"), m_OverwriteExistingSegmentation(false) { } mitk::AutoSegmentationTool::~AutoSegmentationTool() { } const char *mitk::AutoSegmentationTool::GetGroup() const { return "autoSegmentation"; } mitk::Image::ConstPointer mitk::AutoSegmentationTool::Get3DImage(const mitk::Image* image, unsigned int timestep) const { if (nullptr == image) return image; if (image->GetDimension() != 4) return image; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(timestep)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } mitk::Image::ConstPointer mitk::AutoSegmentationTool::Get3DImageByTimePoint(const mitk::Image* image, TimePointType timePoint) const { if (nullptr == image) return image; if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; return this->Get3DImage(image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint)); } void mitk::AutoSegmentationTool::SetOverwriteExistingSegmentation(bool overwrite) { m_OverwriteExistingSegmentation = overwrite; } std::string mitk::AutoSegmentationTool::GetCurrentSegmentationName() { if (m_ToolManager->GetWorkingData(0)) return m_ToolManager->GetWorkingData(0)->GetName(); else return ""; } mitk::DataNode *mitk::AutoSegmentationTool::GetTargetSegmentationNode() { - mitk::DataNode::Pointer emptySegmentation; - if (m_OverwriteExistingSegmentation) - { - emptySegmentation = m_ToolManager->GetWorkingData(0); - } - else + mitk::DataNode::Pointer segmentationNode = m_ToolManager->GetWorkingData(0); + if (!m_OverwriteExistingSegmentation) { mitk::DataNode::Pointer refNode = m_ToolManager->GetReferenceData(0); if (refNode.IsNull()) { // TODO create and use segmentation exceptions instead!! MITK_ERROR << "No valid reference data!"; return nullptr; } - std::string nodename = m_ToolManager->GetReferenceData(0)->GetName() + "_" + this->GetName(); + + std::string nodename = refNode->GetName() + "_" + this->GetName(); mitk::Color color; color.SetRed(1); color.SetBlue(0); color.SetGreen(0); - emptySegmentation = CreateEmptySegmentationNode(dynamic_cast(refNode->GetData()), nodename, color); - m_ToolManager->GetDataStorage()->Add(emptySegmentation, refNode); + //create a new segmentation node based on the current segmentation as template + segmentationNode = CreateEmptySegmentationNode(dynamic_cast(segmentationNode->GetData()), nodename, color); + + m_ToolManager->GetDataStorage()->Add(segmentationNode, refNode); } - return emptySegmentation; + return segmentationNode; } diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp similarity index 55% copy from Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp copy to Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp index ab917f34fe..f48232e055 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp @@ -1,406 +1,466 @@ /*============================================================================ 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 "mitkBinaryThresholdULTool.h" +#include "mitkBinaryThresholdBaseTool.h" #include "mitkToolManager.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageTimeSelector.h" #include "mitkLabelSetImage.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include #include -// us -#include "usGetModuleContext.h" -#include "usModule.h" -#include "usModuleContext.h" -#include "usModuleResource.h" - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, BinaryThresholdULTool, "ThresholdingUL tool"); -} - -mitk::BinaryThresholdULTool::BinaryThresholdULTool() +mitk::BinaryThresholdBaseTool::BinaryThresholdBaseTool() : m_SensibleMinimumThresholdValue(-100), m_SensibleMaximumThresholdValue(+100), m_CurrentLowerThresholdValue(1), m_CurrentUpperThresholdValue(1) { m_ThresholdFeedbackNode = DataNode::New(); m_ThresholdFeedbackNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); m_ThresholdFeedbackNode->SetProperty("name", StringProperty::New("Thresholding feedback")); m_ThresholdFeedbackNode->SetProperty("opacity", FloatProperty::New(0.3)); m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true)); m_ThresholdFeedbackNode->SetProperty("helper object", BoolProperty::New(true)); } -mitk::BinaryThresholdULTool::~BinaryThresholdULTool() +mitk::BinaryThresholdBaseTool::~BinaryThresholdBaseTool() { } -const char **mitk::BinaryThresholdULTool::GetXPM() const -{ - return nullptr; -} - -us::ModuleResource mitk::BinaryThresholdULTool::GetIconResource() const -{ - us::Module *module = us::GetModuleContext()->GetModule(); - us::ModuleResource resource = module->GetResource("TwoThresholds_48x48.png"); - return resource; -} - -const char *mitk::BinaryThresholdULTool::GetName() const -{ - return "UL Threshold"; -} - -void mitk::BinaryThresholdULTool::Activated() +void mitk::BinaryThresholdBaseTool::Activated() { Superclass::Activated(); m_ToolManager->RoiDataChanged += - mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); + mitk::MessageDelegate(this, &mitk::BinaryThresholdBaseTool::OnRoiDataChanged); + + m_ToolManager->SelectedTimePointChanged += + mitk::MessageDelegate(this, &mitk::BinaryThresholdBaseTool::OnTimePointChanged); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeForThresholding = m_OriginalImageNode; if (m_NodeForThresholding.IsNotNull()) { SetupPreviewNode(); } else { m_ToolManager->ActivateTool(-1); } } -void mitk::BinaryThresholdULTool::Deactivated() +void mitk::BinaryThresholdBaseTool::Deactivated() { m_ToolManager->RoiDataChanged -= - mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); + mitk::MessageDelegate(this, &mitk::BinaryThresholdBaseTool::OnRoiDataChanged); + + m_ToolManager->SelectedTimePointChanged -= + mitk::MessageDelegate(this, &mitk::BinaryThresholdBaseTool::OnTimePointChanged); + m_NodeForThresholding = nullptr; m_OriginalImageNode = nullptr; try { if (DataStorage *storage = m_ToolManager->GetDataStorage()) { storage->Remove(m_ThresholdFeedbackNode); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (...) { // don't care } m_ThresholdFeedbackNode->SetData(nullptr); Superclass::Deactivated(); } -void mitk::BinaryThresholdULTool::SetThresholdValues(double lower, double upper) +void mitk::BinaryThresholdBaseTool::SetThresholdValues(double lower, double upper) { + /* If value is not in the min/max range, do nothing. In that case, this + method will be called again with a proper value right after. The only + known case where this happens is with an [0.0, 1.0[ image, where value + could be an epsilon greater than the max. */ + if (lower < m_SensibleMinimumThresholdValue + || lower > m_SensibleMaximumThresholdValue + || upper < m_SensibleMinimumThresholdValue + || upper > m_SensibleMaximumThresholdValue) + { + return; + } + + m_CurrentLowerThresholdValue = lower; + m_CurrentUpperThresholdValue = upper; + if (m_ThresholdFeedbackNode.IsNotNull()) { - m_CurrentLowerThresholdValue = lower; - m_CurrentUpperThresholdValue = upper; + UpdatePreview(); } } -void mitk::BinaryThresholdULTool::AcceptCurrentThresholdValue() +void mitk::BinaryThresholdBaseTool::AcceptCurrentThresholdValue() { - CreateNewSegmentationFromThreshold(m_NodeForThresholding); + CreateNewSegmentationFromThreshold(); RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } -void mitk::BinaryThresholdULTool::CancelThresholding() +void mitk::BinaryThresholdBaseTool::CancelThresholding() { m_ToolManager->ActivateTool(-1); } -void mitk::BinaryThresholdULTool::SetupPreviewNode() +void mitk::BinaryThresholdBaseTool::SetupPreviewNode() { itk::RGBPixel pixel; pixel[0] = 0.0f; pixel[1] = 1.0f; pixel[2] = 0.0f; if (m_NodeForThresholding.IsNotNull()) { Image::Pointer image = dynamic_cast(m_NodeForThresholding->GetData()); Image::Pointer originalImage = dynamic_cast(m_OriginalImageNode->GetData()); if (image.IsNotNull()) { mitk::LabelSetImage::Pointer workingImage = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (workingImage.IsNotNull()) { m_ThresholdFeedbackNode->SetData(workingImage->Clone()); m_IsOldBinary = false; // Let's paint the feedback node green... mitk::LabelSetImage::Pointer previewImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); if (previewImage.IsNull()) { MITK_ERROR << "Cannot create helper objects."; return; } previewImage->GetActiveLabel()->SetColor(pixel); previewImage->GetActiveLabelSet()->UpdateLookupTable(previewImage->GetActiveLabel()->GetValue()); } else { mitk::Image::Pointer workingImageBin = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (workingImageBin) { m_ThresholdFeedbackNode->SetData(workingImageBin->Clone()); m_IsOldBinary = true; } else m_ThresholdFeedbackNode->SetData(mitk::Image::New()); } m_ThresholdFeedbackNode->SetColor(pixel); m_ThresholdFeedbackNode->SetOpacity(0.5); int layer(50); m_NodeForThresholding->GetIntProperty("layer", layer); m_ThresholdFeedbackNode->SetIntProperty("layer", layer + 1); if (DataStorage *ds = m_ToolManager->GetDataStorage()) { if (!ds->Exists(m_ThresholdFeedbackNode)) ds->Add(m_ThresholdFeedbackNode, m_OriginalImageNode); } if (image.GetPointer() == originalImage.GetPointer()) { + m_SensibleMinimumThresholdValue = std::numeric_limits::max(); + m_SensibleMaximumThresholdValue = std::numeric_limits::lowest(); Image::StatisticsHolderPointer statistics = originalImage->GetStatistics(); - m_SensibleMinimumThresholdValue = static_cast(statistics->GetScalarValueMin()); - m_SensibleMaximumThresholdValue = static_cast(statistics->GetScalarValueMax()); + for (unsigned int ts = 0; ts < originalImage->GetTimeSteps(); ++ts) + { + m_SensibleMinimumThresholdValue = std::min(m_SensibleMinimumThresholdValue, static_cast(statistics->GetScalarValueMin())); + m_SensibleMaximumThresholdValue = std::max(m_SensibleMaximumThresholdValue, static_cast(statistics->GetScalarValueMax())); + } } - double range = m_SensibleMaximumThresholdValue - m_SensibleMinimumThresholdValue; - m_CurrentLowerThresholdValue = m_SensibleMinimumThresholdValue + range / 3.0; - m_CurrentUpperThresholdValue = m_SensibleMinimumThresholdValue + 2 * range / 3.0; + if (m_LockedUpperThreshold) + { + m_CurrentLowerThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue) / 2.0; + m_CurrentUpperThresholdValue = m_SensibleMaximumThresholdValue; + } + else + { + double range = m_SensibleMaximumThresholdValue - m_SensibleMinimumThresholdValue; + m_CurrentLowerThresholdValue = m_SensibleMinimumThresholdValue + range / 3.0; + m_CurrentUpperThresholdValue = m_SensibleMinimumThresholdValue + 2 * range / 3.0; + } bool isFloatImage = false; if ((originalImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) && (originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT || originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE)) { isFloatImage = true; } IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue, isFloatImage); ThresholdingValuesChanged.Send(m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue); } } } template -static void ITKSetVolume(itk::Image *originalImage, +static void ITKSetVolume(const itk::Image *originalImage, mitk::Image *segmentation, unsigned int timeStep) { - segmentation->SetVolume((void *)originalImage->GetPixelContainer()->GetBufferPointer(), timeStep); + auto constPixelContainer = originalImage->GetPixelContainer(); + //have to make a const cast because itk::PixelContainer does not provide a const correct access :( + auto pixelContainer = const_cast::PixelContainer*>(constPixelContainer); + + segmentation->SetVolume((void *)pixelContainer->GetBufferPointer(), timeStep); +} + +void mitk::BinaryThresholdBaseTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep) +{ + try + { + Image::ConstPointer image3D = this->Get3DImage(sourceImage, timeStep); + + if (image3D->GetDimension() == 2) + { + AccessFixedDimensionByItk_2( + image3D, ITKSetVolume, 2, destinationImage, timeStep); + } + else + { + AccessFixedDimensionByItk_2( + image3D, ITKSetVolume, 3, destinationImage, timeStep); + } + } + catch (...) + { + Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); + } } -void mitk::BinaryThresholdULTool::CreateNewSegmentationFromThreshold(DataNode *node) +void mitk::BinaryThresholdBaseTool::CreateNewSegmentationFromThreshold() { - if (node) + if (m_NodeForThresholding.IsNotNull() && m_ThresholdFeedbackNode.IsNotNull()) { Image::Pointer feedBackImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); if (feedBackImage.IsNotNull()) { // create a new image of the same dimensions and smallest possible pixel type - DataNode::Pointer emptySegmentation = GetTargetSegmentationNode(); + DataNode::Pointer emptySegmentationNode = GetTargetSegmentationNode(); - if (emptySegmentation) + if (emptySegmentationNode) { - // actually perform a thresholding and ask for an organ type - for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep) + const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + auto emptySegmentation = dynamic_cast(emptySegmentationNode->GetData()); + + // actually perform a thresholding + // REMARK: the following code in this scope assumes that feedBackImage and emptySegmentationImage + // are clones of the working image (segmentation provided to the tool). Therefor the have the same + // time geometry. + if (feedBackImage->GetTimeSteps() != emptySegmentation->GetTimeSteps()) { - try - { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(feedBackImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer feedBackImage3D = timeSelector->GetOutput(); - - if (feedBackImage3D->GetDimension() == 2) - { - AccessFixedDimensionByItk_2( - feedBackImage3D, ITKSetVolume, 2, dynamic_cast(emptySegmentation->GetData()), timeStep); - } - else - { - AccessFixedDimensionByItk_2( - feedBackImage3D, ITKSetVolume, 3, dynamic_cast(emptySegmentation->GetData()), timeStep); - } - } - catch (...) + mitkThrow() << "Cannot performe threshold. Internal tool state is invalid." + << " Preview segmentation and segmentation result image have different time geometries."; + } + + if (m_CreateAllTimeSteps) + { + for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep) { - Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); + TransferImageAtTimeStep(feedBackImage, emptySegmentation, timeStep); } } + else + { + const auto timeStep = emptySegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); + TransferImageAtTimeStep(feedBackImage, emptySegmentation, timeStep); + } // since we are maybe working on a smaller image, pad it to the size of the original image if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); - padFilter->SetInput(0, dynamic_cast(emptySegmentation->GetData())); - padFilter->SetInput(1, dynamic_cast(m_OriginalImageNode->GetData())); + padFilter->SetInput(0, emptySegmentation); + padFilter->SetInput(1, dynamic_cast(m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); - emptySegmentation->SetData(padFilter->GetOutput()); + emptySegmentationNode->SetData(padFilter->GetOutput()); } - m_ToolManager->SetWorkingData(emptySegmentation); + m_ToolManager->SetWorkingData(emptySegmentationNode); m_ToolManager->GetWorkingData(0)->Modified(); } } } } -void mitk::BinaryThresholdULTool::OnRoiDataChanged() +void mitk::BinaryThresholdBaseTool::OnRoiDataChanged() { mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); mitk::Image::Pointer image = dynamic_cast(m_NodeForThresholding->GetData()); if (image.IsNull()) return; roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); tmpNode->SetData(roiFilter->GetOutput()); m_SensibleMinimumThresholdValue = static_cast(roiFilter->GetMinValue()); m_SensibleMaximumThresholdValue = static_cast(roiFilter->GetMaxValue()); m_NodeForThresholding = tmpNode; } else m_NodeForThresholding = m_OriginalImageNode; this->SetupPreviewNode(); this->UpdatePreview(); } +void mitk::BinaryThresholdBaseTool::OnTimePointChanged() +{ + if (m_ThresholdFeedbackNode.IsNotNull() && m_NodeForThresholding.IsNotNull()) + { + if (m_ThresholdFeedbackNode->GetData()->GetTimeSteps() == 1) + { + this->UpdatePreview(); + } + } +} + template -static void ITKThresholding(itk::Image *originalImage, +static void ITKThresholding(const itk::Image *originalImage, mitk::Image *segmentation, double lower, double upper, unsigned int timeStep) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(originalImage); filter->SetLowerThreshold(lower); filter->SetUpperThreshold(upper); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); } template -static void ITKThresholdingOldBinary(itk::Image *originalImage, +static void ITKThresholdingOldBinary(const itk::Image *originalImage, mitk::Image *segmentation, double lower, double upper, unsigned int timeStep) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(originalImage); filter->SetLowerThreshold(lower); filter->SetUpperThreshold(upper); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); } -void mitk::BinaryThresholdULTool::UpdatePreview() +void mitk::BinaryThresholdBaseTool::UpdatePreview() { mitk::Image::Pointer thresholdImage = dynamic_cast(m_NodeForThresholding->GetData()); mitk::Image::Pointer previewImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); if (thresholdImage && previewImage) { - for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep) + if (previewImage->GetTimeSteps() > 1) + { + for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep) + { + auto feedBackImage3D = this->Get3DImage(thresholdImage, timeStep); + + if (m_IsOldBinary) + { + AccessByItk_n(feedBackImage3D, + ITKThresholdingOldBinary, + (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); + } + else + { + AccessByItk_n(feedBackImage3D, + ITKThresholding, + (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); + } + } + } + else { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(thresholdImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer feedBackImage3D = timeSelector->GetOutput(); + const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + auto feedBackImage3D = this->Get3DImageByTimePoint(thresholdImage, timePoint); + auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); if (m_IsOldBinary) { AccessByItk_n(feedBackImage3D, - ITKThresholdingOldBinary, - (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); + ITKThresholdingOldBinary, + (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); } else { AccessByItk_n(feedBackImage3D, - ITKThresholding, - (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); + ITKThresholding, + (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); } } RenderingManager::GetInstance()->RequestUpdateAll(); } } diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h new file mode 100644 index 0000000000..dece9ae498 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.h @@ -0,0 +1,98 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkBinaryThresholdBaseTool_h_Included +#define mitkBinaryThresholdBaseTool_h_Included + +#include "mitkAutoSegmentationTool.h" +#include "mitkCommon.h" +#include "mitkDataNode.h" +#include + +#include +#include + +namespace mitk +{ + /** + \brief Base class for binary threshold tools. + + \ingroup ToolManagerEtAl + \sa mitk::Tool + \sa QmitkInteractiveSegmentation + */ + class MITKSEGMENTATION_EXPORT BinaryThresholdBaseTool : public AutoSegmentationTool + { + public: + Message3 IntervalBordersChanged; + Message2 ThresholdingValuesChanged; + + mitkClassMacro(BinaryThresholdBaseTool, AutoSegmentationTool); + + void Activated() override; + void Deactivated() override; + + virtual void SetThresholdValues(double lower, double upper); + + virtual void AcceptCurrentThresholdValue(); + virtual void CancelThresholding(); + + itkSetMacro(CreateAllTimeSteps, bool); + itkGetMacro(CreateAllTimeSteps, bool); + itkBooleanMacro(CreateAllTimeSteps); + + protected: + BinaryThresholdBaseTool(); // purposely hidden + ~BinaryThresholdBaseTool() override; + + itkSetMacro(LockedUpperThreshold, bool); + itkGetMacro(LockedUpperThreshold, bool); + itkBooleanMacro(LockedUpperThreshold); + + itkGetMacro(SensibleMinimumThresholdValue, ScalarType); + itkGetMacro(SensibleMaximumThresholdValue, ScalarType); + + private: + void SetupPreviewNode(); + + void TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep); + + void CreateNewSegmentationFromThreshold(); + + void OnRoiDataChanged(); + void OnTimePointChanged(); + + void UpdatePreview(); + + DataNode::Pointer m_ThresholdFeedbackNode; + DataNode::Pointer m_OriginalImageNode; + DataNode::Pointer m_NodeForThresholding; + + ScalarType m_SensibleMinimumThresholdValue; + ScalarType m_SensibleMaximumThresholdValue; + ScalarType m_CurrentLowerThresholdValue; + ScalarType m_CurrentUpperThresholdValue; + + bool m_IsOldBinary = false; + + /** Indicates if Accepting the threshold should transfer/create the segmentations + of all time steps (true) or only of the currently selected timepoint (false).*/ + bool m_CreateAllTimeSteps = false; + + /** Indicates if the tool should behave like a single threshold tool (true) + or like a upper/lower threshold tool (false)*/ + bool m_LockedUpperThreshold = false; + }; + +} // namespace + +#endif diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp index 5ade4cb9f4..332383f5e6 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp @@ -1,418 +1,55 @@ /*============================================================================ 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 "mitkBinaryThresholdTool.h" -#include "mitkBoundingObjectToSegmentationFilter.h" -#include "mitkToolManager.h" - -#include "mitkColorProperty.h" -#include "mitkDataStorage.h" -#include "mitkLevelWindowProperty.h" -#include "mitkProperties.h" -#include "mitkRenderingManager.h" -#include "mitkVtkResliceInterpolationProperty.h" -#include - -#include "mitkImageAccessByItk.h" -#include "mitkImageCast.h" -#include "mitkImageStatisticsHolder.h" -#include "mitkImageTimeSelector.h" -#include "mitkLabelSetImage.h" -#include "mitkMaskAndCutRoiImageFilter.h" -#include "mitkPadImageFilter.h" -#include -#include - // us #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleResource.h" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, BinaryThresholdTool, "Thresholding tool"); } -mitk::BinaryThresholdTool::BinaryThresholdTool() - : m_SensibleMinimumThresholdValue(-100), - m_SensibleMaximumThresholdValue(+100), - m_CurrentThresholdValue(0.0), - m_IsFloatImage(false) +mitk::BinaryThresholdTool::BinaryThresholdTool() : BinaryThresholdBaseTool() { - m_ThresholdFeedbackNode = DataNode::New(); - m_ThresholdFeedbackNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); - m_ThresholdFeedbackNode->SetProperty("name", StringProperty::New("Thresholding feedback")); - m_ThresholdFeedbackNode->SetProperty("opacity", FloatProperty::New(0.3)); - m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true)); - m_ThresholdFeedbackNode->SetProperty("helper object", BoolProperty::New(true)); + LockedUpperThresholdOn(); } mitk::BinaryThresholdTool::~BinaryThresholdTool() { } const char **mitk::BinaryThresholdTool::GetXPM() const { return nullptr; } us::ModuleResource mitk::BinaryThresholdTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Threshold_48x48.png"); return resource; } const char *mitk::BinaryThresholdTool::GetName() const { return "Threshold"; } -void mitk::BinaryThresholdTool::Activated() -{ - Superclass::Activated(); - - m_ToolManager->RoiDataChanged += - mitk::MessageDelegate(this, &mitk::BinaryThresholdTool::OnRoiDataChanged); - - m_OriginalImageNode = m_ToolManager->GetReferenceData(0); - m_NodeForThresholding = m_OriginalImageNode; - - if (m_NodeForThresholding.IsNotNull()) - { - SetupPreviewNode(); - } - else - { - m_ToolManager->ActivateTool(-1); - } -} - -void mitk::BinaryThresholdTool::Deactivated() -{ - m_ToolManager->RoiDataChanged -= - mitk::MessageDelegate(this, &mitk::BinaryThresholdTool::OnRoiDataChanged); - m_NodeForThresholding = nullptr; - m_OriginalImageNode = nullptr; - try - { - if (DataStorage *storage = m_ToolManager->GetDataStorage()) - { - storage->Remove(m_ThresholdFeedbackNode); - RenderingManager::GetInstance()->RequestUpdateAll(); - } - } - catch (...) - { - // don't care - } - m_ThresholdFeedbackNode->SetData(nullptr); - - Superclass::Deactivated(); -} void mitk::BinaryThresholdTool::SetThresholdValue(double value) { - if (m_ThresholdFeedbackNode.IsNotNull()) - { - /* If value is not in the min/max range, do nothing. In that case, this - method will be called again with a proper value right after. The only - known case where this happens is with an [0.0, 1.0[ image, where value - could be an epsilon greater than the max. */ - if (value < m_SensibleMinimumThresholdValue - || value > m_SensibleMaximumThresholdValue) - { - return; - } - - m_CurrentThresholdValue = value; - // Bug 19250: The range of 0.01 is rather random. It was 0.001 before and probably due to rounding error propagation - // in VTK code - // it leads to strange banding effects on floating point images with a huge range (like -40000 - 40000). 0.01 lowers - // this effect - // enough to work with our images. Might not work on images with really huge ranges, though. Anyways, still seems to - // be low enough - // to work for floating point images with a range between 0 and 1. A better solution might be to dynamically - // calculate the value - // based on the value range of the current image (as big as possible, as small as necessary). - // m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( - // LevelWindow(m_CurrentThresholdValue, 0.01) ) ); - UpdatePreview(); - } -} - -void mitk::BinaryThresholdTool::AcceptCurrentThresholdValue() -{ - CreateNewSegmentationFromThreshold(m_NodeForThresholding); - - RenderingManager::GetInstance()->RequestUpdateAll(); - m_ToolManager->ActivateTool(-1); -} - -void mitk::BinaryThresholdTool::CancelThresholding() -{ - m_ToolManager->ActivateTool(-1); -} - -void mitk::BinaryThresholdTool::SetupPreviewNode() -{ - itk::RGBPixel pixel; - pixel[0] = 0.0f; - pixel[1] = 1.0f; - pixel[2] = 0.0f; - - if (m_NodeForThresholding.IsNotNull()) - { - Image::Pointer image = dynamic_cast(m_NodeForThresholding->GetData()); - Image::Pointer originalImage = dynamic_cast(m_OriginalImageNode->GetData()); - - if (image.IsNotNull()) - { - mitk::LabelSetImage::Pointer workingImage = - dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); - - if (workingImage.IsNotNull()) - { - m_ThresholdFeedbackNode->SetData(workingImage->Clone()); - m_IsOldBinary = false; - - // Let's paint the feedback node green... - mitk::LabelSetImage::Pointer previewImage = - dynamic_cast(m_ThresholdFeedbackNode->GetData()); - - if (previewImage.IsNull()) - { - MITK_ERROR << "Cannot create helper objects."; - return; - } - - previewImage->GetActiveLabel()->SetColor(pixel); - previewImage->GetActiveLabelSet()->UpdateLookupTable(previewImage->GetActiveLabel()->GetValue()); - } - else - { - mitk::Image::Pointer workingImageBin = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); - if (workingImageBin) - { - m_ThresholdFeedbackNode->SetData(workingImageBin->Clone()); - m_IsOldBinary = true; - } - else - m_ThresholdFeedbackNode->SetData(mitk::Image::New()); - } - - m_ThresholdFeedbackNode->SetColor(pixel); - m_ThresholdFeedbackNode->SetOpacity(0.5); - - int layer(50); - m_NodeForThresholding->GetIntProperty("layer", layer); - m_ThresholdFeedbackNode->SetIntProperty("layer", layer + 1); - - if (DataStorage *ds = m_ToolManager->GetDataStorage()) - { - if (!ds->Exists(m_ThresholdFeedbackNode)) - ds->Add(m_ThresholdFeedbackNode, m_OriginalImageNode); - } - - if (image.GetPointer() == originalImage.GetPointer()) - { - Image::StatisticsHolderPointer statistics = originalImage->GetStatistics(); - m_SensibleMinimumThresholdValue = static_cast(statistics->GetScalarValueMin()); - m_SensibleMaximumThresholdValue = static_cast(statistics->GetScalarValueMax()); - } - - if ((originalImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) && - (originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT || - originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE)) - m_IsFloatImage = true; - else - m_IsFloatImage = false; - - m_CurrentThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue) / 2.0; - - IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue, m_IsFloatImage); - ThresholdingValueChanged.Send(m_CurrentThresholdValue); - } - } -} - -template -static void ITKSetVolume(itk::Image *originalImage, - mitk::Image *segmentation, - unsigned int timeStep) -{ - segmentation->SetVolume((void *)originalImage->GetPixelContainer()->GetBufferPointer(), timeStep); -} - -void mitk::BinaryThresholdTool::CreateNewSegmentationFromThreshold(DataNode *node) -{ - if (node) - { - Image::Pointer feedBackImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); - if (feedBackImage.IsNotNull()) - { - DataNode::Pointer emptySegmentation = GetTargetSegmentationNode(); - - if (emptySegmentation) - { - // actually perform a thresholding and ask for an organ type - for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep) - { - try - { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(feedBackImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer image3D = timeSelector->GetOutput(); - - if (image3D->GetDimension() == 2) - { - AccessFixedDimensionByItk_2( - image3D, ITKSetVolume, 2, dynamic_cast(emptySegmentation->GetData()), timeStep); - } - else - { - AccessFixedDimensionByItk_2( - image3D, ITKSetVolume, 3, dynamic_cast(emptySegmentation->GetData()), timeStep); - } - } - catch (...) - { - Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); - } - } - - if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) - { - mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); - - padFilter->SetInput(0, dynamic_cast(emptySegmentation->GetData())); - padFilter->SetInput(1, dynamic_cast(m_OriginalImageNode->GetData())); - padFilter->SetBinaryFilter(true); - padFilter->SetUpperThreshold(1); - padFilter->SetLowerThreshold(1); - padFilter->Update(); - - emptySegmentation->SetData(padFilter->GetOutput()); - } - - m_ToolManager->SetWorkingData(emptySegmentation); - m_ToolManager->GetWorkingData(0)->Modified(); - } - } - } -} - -void mitk::BinaryThresholdTool::OnRoiDataChanged() -{ - mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); - - if (node.IsNotNull()) - { - mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); - mitk::Image::Pointer image = dynamic_cast(m_NodeForThresholding->GetData()); - - if (image.IsNull()) - return; - - roiFilter->SetInput(image); - roiFilter->SetRegionOfInterest(node->GetData()); - roiFilter->Update(); - - mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); - tmpNode->SetData(roiFilter->GetOutput()); - - m_SensibleMaximumThresholdValue = static_cast(roiFilter->GetMaxValue()); - m_SensibleMinimumThresholdValue = static_cast(roiFilter->GetMinValue()); - - m_NodeForThresholding = tmpNode; - } - else - { - m_NodeForThresholding = m_OriginalImageNode; - } - - this->SetupPreviewNode(); - this->UpdatePreview(); -} - -template -void mitk::BinaryThresholdTool::ITKThresholding(itk::Image *originalImage, - Image *segmentation, - double thresholdValue, - unsigned int timeStep) -{ - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - - typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); - filter->SetInput(originalImage); - filter->SetLowerThreshold(thresholdValue); - filter->SetUpperThreshold(m_SensibleMaximumThresholdValue); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->Update(); - - segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); -} - -template -void mitk::BinaryThresholdTool::ITKThresholdingOldBinary(itk::Image *originalImage, - Image *segmentation, - double thresholdValue, - unsigned int timeStep) -{ - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - - typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); - filter->SetInput(originalImage); - filter->SetLowerThreshold(thresholdValue); - filter->SetUpperThreshold(m_SensibleMaximumThresholdValue); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->Update(); - - segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); -} - -void mitk::BinaryThresholdTool::UpdatePreview() -{ - mitk::Image::Pointer thresholdImage = dynamic_cast(m_NodeForThresholding->GetData()); - mitk::Image::Pointer previewImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); - if (thresholdImage && previewImage) - { - for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep) - { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(thresholdImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer feedBackImage3D = timeSelector->GetOutput(); - - if (m_IsOldBinary) - { - AccessByItk_n(feedBackImage3D, ITKThresholdingOldBinary, (previewImage, m_CurrentThresholdValue, timeStep)); - } - else - { - AccessByItk_n(feedBackImage3D, ITKThresholding, (previewImage, m_CurrentThresholdValue, timeStep)); - } - } - - RenderingManager::GetInstance()->RequestUpdateAll(); - } + this->SetThresholdValues(value, this->GetSensibleMaximumThresholdValue()); } diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h index e2a8d712e8..aed3e99328 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.h @@ -1,96 +1,56 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkBinaryThresholdTool_h_Included #define mitkBinaryThresholdTool_h_Included -#include "mitkAutoSegmentationTool.h" -#include "mitkCommon.h" -#include "mitkDataNode.h" +#include "mitkBinaryThresholdBaseTool.h" #include -#include - namespace us { class ModuleResource; } namespace mitk { /** \brief Calculates the segmented volumes for binary images. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ - class MITKSEGMENTATION_EXPORT BinaryThresholdTool : public AutoSegmentationTool + class MITKSEGMENTATION_EXPORT BinaryThresholdTool : public BinaryThresholdBaseTool { public: - Message3 IntervalBordersChanged; - Message1 ThresholdingValueChanged; - mitkClassMacro(BinaryThresholdTool, AutoSegmentationTool); + mitkClassMacro(BinaryThresholdTool, BinaryThresholdBaseTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - const char **GetXPM() const override; + const char **GetXPM() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; - void Activated() override; - void Deactivated() override; - virtual void SetThresholdValue(double value); - virtual void AcceptCurrentThresholdValue(); - virtual void CancelThresholding(); protected: BinaryThresholdTool(); // purposely hidden ~BinaryThresholdTool() override; - - void SetupPreviewNode(); - - void CreateNewSegmentationFromThreshold(DataNode *node); - - void OnRoiDataChanged(); - void UpdatePreview(); - - template - void ITKThresholding(itk::Image *originalImage, - mitk::Image *segmentation, - double thresholdValue, - unsigned int timeStep); - template - void ITKThresholdingOldBinary(itk::Image *originalImage, - mitk::Image *segmentation, - double thresholdValue, - unsigned int timeStep); - - DataNode::Pointer m_ThresholdFeedbackNode; - DataNode::Pointer m_OriginalImageNode; - DataNode::Pointer m_NodeForThresholding; - - double m_SensibleMinimumThresholdValue; - double m_SensibleMaximumThresholdValue; - double m_CurrentThresholdValue; - bool m_IsFloatImage; - - bool m_IsOldBinary = false; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp index ab917f34fe..7e729be00e 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.cpp @@ -1,406 +1,50 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBinaryThresholdULTool.h" -#include "mitkToolManager.h" - -#include "mitkColorProperty.h" -#include "mitkLevelWindowProperty.h" -#include "mitkProperties.h" - -#include "mitkDataStorage.h" -#include "mitkRenderingManager.h" -#include - -#include "mitkImageAccessByItk.h" -#include "mitkImageCast.h" -#include "mitkImageStatisticsHolder.h" -#include "mitkImageTimeSelector.h" -#include "mitkLabelSetImage.h" -#include "mitkMaskAndCutRoiImageFilter.h" -#include "mitkPadImageFilter.h" -#include -#include - // us #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleResource.h" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, BinaryThresholdULTool, "ThresholdingUL tool"); } -mitk::BinaryThresholdULTool::BinaryThresholdULTool() - : m_SensibleMinimumThresholdValue(-100), - m_SensibleMaximumThresholdValue(+100), - m_CurrentLowerThresholdValue(1), - m_CurrentUpperThresholdValue(1) +mitk::BinaryThresholdULTool::BinaryThresholdULTool() : BinaryThresholdBaseTool() { - m_ThresholdFeedbackNode = DataNode::New(); - m_ThresholdFeedbackNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); - m_ThresholdFeedbackNode->SetProperty("name", StringProperty::New("Thresholding feedback")); - m_ThresholdFeedbackNode->SetProperty("opacity", FloatProperty::New(0.3)); - m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true)); - m_ThresholdFeedbackNode->SetProperty("helper object", BoolProperty::New(true)); } mitk::BinaryThresholdULTool::~BinaryThresholdULTool() { } const char **mitk::BinaryThresholdULTool::GetXPM() const { return nullptr; } us::ModuleResource mitk::BinaryThresholdULTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("TwoThresholds_48x48.png"); return resource; } const char *mitk::BinaryThresholdULTool::GetName() const { return "UL Threshold"; } -void mitk::BinaryThresholdULTool::Activated() -{ - Superclass::Activated(); - - m_ToolManager->RoiDataChanged += - mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); - - m_OriginalImageNode = m_ToolManager->GetReferenceData(0); - m_NodeForThresholding = m_OriginalImageNode; - - if (m_NodeForThresholding.IsNotNull()) - { - SetupPreviewNode(); - } - else - { - m_ToolManager->ActivateTool(-1); - } -} - -void mitk::BinaryThresholdULTool::Deactivated() -{ - m_ToolManager->RoiDataChanged -= - mitk::MessageDelegate(this, &mitk::BinaryThresholdULTool::OnRoiDataChanged); - m_NodeForThresholding = nullptr; - m_OriginalImageNode = nullptr; - try - { - if (DataStorage *storage = m_ToolManager->GetDataStorage()) - { - storage->Remove(m_ThresholdFeedbackNode); - RenderingManager::GetInstance()->RequestUpdateAll(); - } - } - catch (...) - { - // don't care - } - m_ThresholdFeedbackNode->SetData(nullptr); - - Superclass::Deactivated(); -} - -void mitk::BinaryThresholdULTool::SetThresholdValues(double lower, double upper) -{ - if (m_ThresholdFeedbackNode.IsNotNull()) - { - m_CurrentLowerThresholdValue = lower; - m_CurrentUpperThresholdValue = upper; - UpdatePreview(); - } -} - -void mitk::BinaryThresholdULTool::AcceptCurrentThresholdValue() -{ - CreateNewSegmentationFromThreshold(m_NodeForThresholding); - - RenderingManager::GetInstance()->RequestUpdateAll(); - m_ToolManager->ActivateTool(-1); -} - -void mitk::BinaryThresholdULTool::CancelThresholding() -{ - m_ToolManager->ActivateTool(-1); -} - -void mitk::BinaryThresholdULTool::SetupPreviewNode() -{ - itk::RGBPixel pixel; - pixel[0] = 0.0f; - pixel[1] = 1.0f; - pixel[2] = 0.0f; - - if (m_NodeForThresholding.IsNotNull()) - { - Image::Pointer image = dynamic_cast(m_NodeForThresholding->GetData()); - Image::Pointer originalImage = dynamic_cast(m_OriginalImageNode->GetData()); - - if (image.IsNotNull()) - { - mitk::LabelSetImage::Pointer workingImage = - dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); - - if (workingImage.IsNotNull()) - { - m_ThresholdFeedbackNode->SetData(workingImage->Clone()); - m_IsOldBinary = false; - - // Let's paint the feedback node green... - mitk::LabelSetImage::Pointer previewImage = - dynamic_cast(m_ThresholdFeedbackNode->GetData()); - - if (previewImage.IsNull()) - { - MITK_ERROR << "Cannot create helper objects."; - return; - } - - previewImage->GetActiveLabel()->SetColor(pixel); - previewImage->GetActiveLabelSet()->UpdateLookupTable(previewImage->GetActiveLabel()->GetValue()); - } - else - { - mitk::Image::Pointer workingImageBin = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); - if (workingImageBin) - { - m_ThresholdFeedbackNode->SetData(workingImageBin->Clone()); - m_IsOldBinary = true; - } - else - m_ThresholdFeedbackNode->SetData(mitk::Image::New()); - } - - m_ThresholdFeedbackNode->SetColor(pixel); - m_ThresholdFeedbackNode->SetOpacity(0.5); - - int layer(50); - m_NodeForThresholding->GetIntProperty("layer", layer); - m_ThresholdFeedbackNode->SetIntProperty("layer", layer + 1); - - if (DataStorage *ds = m_ToolManager->GetDataStorage()) - { - if (!ds->Exists(m_ThresholdFeedbackNode)) - ds->Add(m_ThresholdFeedbackNode, m_OriginalImageNode); - } - - if (image.GetPointer() == originalImage.GetPointer()) - { - Image::StatisticsHolderPointer statistics = originalImage->GetStatistics(); - m_SensibleMinimumThresholdValue = static_cast(statistics->GetScalarValueMin()); - m_SensibleMaximumThresholdValue = static_cast(statistics->GetScalarValueMax()); - } - - double range = m_SensibleMaximumThresholdValue - m_SensibleMinimumThresholdValue; - m_CurrentLowerThresholdValue = m_SensibleMinimumThresholdValue + range / 3.0; - m_CurrentUpperThresholdValue = m_SensibleMinimumThresholdValue + 2 * range / 3.0; - - bool isFloatImage = false; - if ((originalImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) && - (originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT || - originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE)) - { - isFloatImage = true; - } - - IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue, isFloatImage); - ThresholdingValuesChanged.Send(m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue); - } - } -} - -template -static void ITKSetVolume(itk::Image *originalImage, - mitk::Image *segmentation, - unsigned int timeStep) -{ - segmentation->SetVolume((void *)originalImage->GetPixelContainer()->GetBufferPointer(), timeStep); -} - -void mitk::BinaryThresholdULTool::CreateNewSegmentationFromThreshold(DataNode *node) -{ - if (node) - { - Image::Pointer feedBackImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); - if (feedBackImage.IsNotNull()) - { - // create a new image of the same dimensions and smallest possible pixel type - DataNode::Pointer emptySegmentation = GetTargetSegmentationNode(); - - if (emptySegmentation) - { - // actually perform a thresholding and ask for an organ type - for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep) - { - try - { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(feedBackImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer feedBackImage3D = timeSelector->GetOutput(); - - if (feedBackImage3D->GetDimension() == 2) - { - AccessFixedDimensionByItk_2( - feedBackImage3D, ITKSetVolume, 2, dynamic_cast(emptySegmentation->GetData()), timeStep); - } - else - { - AccessFixedDimensionByItk_2( - feedBackImage3D, ITKSetVolume, 3, dynamic_cast(emptySegmentation->GetData()), timeStep); - } - } - catch (...) - { - Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); - } - } - - // since we are maybe working on a smaller image, pad it to the size of the original image - if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) - { - mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); - - padFilter->SetInput(0, dynamic_cast(emptySegmentation->GetData())); - padFilter->SetInput(1, dynamic_cast(m_OriginalImageNode->GetData())); - padFilter->SetBinaryFilter(true); - padFilter->SetUpperThreshold(1); - padFilter->SetLowerThreshold(1); - padFilter->Update(); - - emptySegmentation->SetData(padFilter->GetOutput()); - } - - m_ToolManager->SetWorkingData(emptySegmentation); - m_ToolManager->GetWorkingData(0)->Modified(); - } - } - } -} - -void mitk::BinaryThresholdULTool::OnRoiDataChanged() -{ - mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); - - if (node.IsNotNull()) - { - mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); - mitk::Image::Pointer image = dynamic_cast(m_NodeForThresholding->GetData()); - - if (image.IsNull()) - return; - - roiFilter->SetInput(image); - roiFilter->SetRegionOfInterest(node->GetData()); - roiFilter->Update(); - - mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); - tmpNode->SetData(roiFilter->GetOutput()); - - m_SensibleMinimumThresholdValue = static_cast(roiFilter->GetMinValue()); - m_SensibleMaximumThresholdValue = static_cast(roiFilter->GetMaxValue()); - - m_NodeForThresholding = tmpNode; - } - else - m_NodeForThresholding = m_OriginalImageNode; - - this->SetupPreviewNode(); - this->UpdatePreview(); -} - -template -static void ITKThresholding(itk::Image *originalImage, - mitk::Image *segmentation, - double lower, - double upper, - unsigned int timeStep) -{ - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - - typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); - filter->SetInput(originalImage); - filter->SetLowerThreshold(lower); - filter->SetUpperThreshold(upper); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->Update(); - - segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); -} - -template -static void ITKThresholdingOldBinary(itk::Image *originalImage, - mitk::Image *segmentation, - double lower, - double upper, - unsigned int timeStep) -{ - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - - typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); - filter->SetInput(originalImage); - filter->SetLowerThreshold(lower); - filter->SetUpperThreshold(upper); - filter->SetInsideValue(1); - filter->SetOutsideValue(0); - filter->Update(); - - segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); -} - -void mitk::BinaryThresholdULTool::UpdatePreview() -{ - mitk::Image::Pointer thresholdImage = dynamic_cast(m_NodeForThresholding->GetData()); - mitk::Image::Pointer previewImage = dynamic_cast(m_ThresholdFeedbackNode->GetData()); - if (thresholdImage && previewImage) - { - for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep) - { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(thresholdImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer feedBackImage3D = timeSelector->GetOutput(); - - if (m_IsOldBinary) - { - AccessByItk_n(feedBackImage3D, - ITKThresholdingOldBinary, - (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); - } - else - { - AccessByItk_n(feedBackImage3D, - ITKThresholding, - (previewImage, m_CurrentLowerThresholdValue, m_CurrentUpperThresholdValue, timeStep)); - } - } - RenderingManager::GetInstance()->RequestUpdateAll(); - } -} diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h index a29b5f3f7d..b6f15824a2 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdULTool.h @@ -1,91 +1,54 @@ /*============================================================================ 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 mitkBinaryThresholdULTool_h_Included #define mitkBinaryThresholdULTool_h_Included -#include "mitkAutoSegmentationTool.h" -#include "mitkCommon.h" -#include "mitkDataNode.h" +#include "mitkBinaryThresholdBaseTool.h" #include -#include -#include namespace us { class ModuleResource; } namespace mitk { /** \brief Calculates the segmented volumes for binary images. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation Last contributor: $Author$ */ - class MITKSEGMENTATION_EXPORT BinaryThresholdULTool : public AutoSegmentationTool + class MITKSEGMENTATION_EXPORT BinaryThresholdULTool : public BinaryThresholdBaseTool { public: - Message3 IntervalBordersChanged; - Message2 ThresholdingValuesChanged; - - mitkClassMacro(BinaryThresholdULTool, AutoSegmentationTool); + mitkClassMacro(BinaryThresholdULTool, BinaryThresholdBaseTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - const char **GetXPM() const override; + const char **GetXPM() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; - void Activated() override; - void Deactivated() override; - - virtual void SetThresholdValues(double lower, double upper); - virtual void AcceptCurrentThresholdValue(); - virtual void CancelThresholding(); - protected: BinaryThresholdULTool(); // purposely hidden ~BinaryThresholdULTool() override; - - void SetupPreviewNode(); - - void CreateNewSegmentationFromThreshold(DataNode *node); - - void OnRoiDataChanged(); - void UpdatePreview(); - - DataNode::Pointer m_ThresholdFeedbackNode; - DataNode::Pointer m_OriginalImageNode; - DataNode::Pointer m_NodeForThresholding; - - mitk::ScalarType m_SensibleMinimumThresholdValue; - mitk::ScalarType m_SensibleMaximumThresholdValue; - mitk::ScalarType m_CurrentLowerThresholdValue; - mitk::ScalarType m_CurrentUpperThresholdValue; - - bool m_IsOldBinary = false; - - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; // this is sure for new segmentations - typedef itk::BinaryThresholdImageFilter ThresholdFilterType; - ThresholdFilterType::Pointer m_ThresholdFilter; }; } // namespace #endif diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 071e038786..676362200a 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,124 +1,116 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkContourModelSetToImageFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDiffSliceOperation.cpp Algorithms/mitkDiffSliceOperationApplier.cpp Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp #Algorithms/mitkImageToContourModelFilter.cpp Algorithms/mitkImageToLiveWireContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkOverwriteSliceImageFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkVtkImageOverwrite.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkToolManager.cpp Controllers/mitkSegmentationModuleActivator.cpp Controllers/mitkToolManagerProvider.cpp DataManagement/mitkContour.cpp - #DataManagement/mitkContourElement.cpp - #DataManagement/mitkContourModel.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAdaptiveRegionGrowingTool.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp + Interactions/mitkBinaryThresholdBaseTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp Interactions/mitkLiveWireTool2D.cpp Interactions/mitkContourTool.cpp Interactions/mitkCorrectorTool2D.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkFastMarchingTool.cpp Interactions/mitkFastMarchingTool3D.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkPixelManipulationTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp Interactions/mitkWatershedTool.cpp Interactions/mitkPickingTool.cpp Interactions/mitkSegmentationInteractor.cpp #SO - #IO/mitkContourModelIOFactory.cpp - #IO/mitkContourModelReader.cpp - #IO/mitkContourModelWriter.cpp - #IO/mitkContourModelWriterFactory.cpp Rendering/mitkContourMapper2D.cpp - #Rendering/mitkContourModelGLMapper2D.cpp - #Rendering/mitkContourModelMapper2D.cpp - #Rendering/mitkContourModelMapper3D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp #Added from ML Controllers/mitkSliceBasedInterpolationController.cpp Algorithms/mitkSurfaceStampImageFilter.cpp ) set(RESOURCE_FILES Add_48x48.png Add_Cursor_32x32.png Correction_48x48.png Correction_Cursor_32x32.png Erase_48x48.png Erase_Cursor_32x32.png FastMarching_48x48.png FastMarching_Cursor_32x32.png Fill_48x48.png Fill_Cursor_32x32.png LiveWire_48x48.png LiveWire_Cursor_32x32.png Otsu_48x48.png Paint_48x48.png Paint_Cursor_32x32.png Pick_48x48.png RegionGrowing_48x48.png RegionGrowing_Cursor_32x32.png Subtract_48x48.png Subtract_Cursor_32x32.png Threshold_48x48.png TwoThresholds_48x48.png Watershed_48x48.png Watershed_Cursor_32x32.png Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml Interactions/LiveWireTool.xml Interactions/FastMarchingTool.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationToolsConfig.xml Interactions/ContourModelModificationConfig.xml Interactions/ContourModelModificationInteractor.xml ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.cpp index b950c20c1c..d26c729fa2 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.cpp @@ -1,212 +1,144 @@ /*============================================================================ 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 "QmitkBinaryThresholdToolGUI.h" -#include "QmitkConfirmSegmentationDialog.h" -#include "QmitkNewSegmentationDialog.h" #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkBinaryThresholdToolGUI, "") QmitkBinaryThresholdToolGUI::QmitkBinaryThresholdToolGUI() - : QmitkToolGUI(), - m_Slider(nullptr), - m_Spinner(nullptr), - m_isFloat(false), - m_RangeMin(0), - m_RangeMax(0), - m_ChangingSlider(false), - m_ChangingSpinner(false) + : QmitkToolGUI() { // create the visible widgets QBoxLayout *mainLayout = new QVBoxLayout(this); QLabel *label = new QLabel("Threshold :", this); QFont f = label->font(); f.setBold(false); label->setFont(f); mainLayout->addWidget(label); QBoxLayout *layout = new QHBoxLayout(); - m_Spinner = new QDoubleSpinBox(); - m_Spinner->setMaximum(20); - m_Spinner->setMinimum(5); - m_Spinner->setValue(1); - - connect(m_Spinner, SIGNAL(valueChanged(double)), this, SLOT(OnSpinnerValueChanged())); - layout->addWidget(m_Spinner); - - // m_Slider = new QSlider( 5, 20, 1, 1, Qt::Horizontal, this ); - m_Slider = new QSlider(Qt::Horizontal, this); - m_Slider->setMinimum(5); - m_Slider->setMaximum(20); - m_Slider->setPageStep(1); - m_Slider->setValue(1); - connect(m_Slider, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); - layout->addWidget(m_Slider); - + m_ThresholdSlider = new ctkSliderWidget(); + connect( + m_ThresholdSlider, SIGNAL(valueChanged(double)), this, SLOT(OnSliderValueChanged(double))); + layout->addWidget(m_ThresholdSlider); mainLayout->addLayout(layout); + m_ThresholdSlider->setSingleStep(0.01); QPushButton *okButton = new QPushButton("Confirm Segmentation", this); connect(okButton, SIGNAL(clicked()), this, SLOT(OnAcceptThresholdPreview())); okButton->setFont(f); mainLayout->addWidget(okButton); + m_CheckProcessAll = new QCheckBox("Process all time steps", this); + m_CheckProcessAll->setChecked(false); + mainLayout->addWidget(m_CheckProcessAll); + + m_CheckCreateNew = new QCheckBox("Create as new segmentation", this); + m_CheckCreateNew->setChecked(false); + mainLayout->addWidget(m_CheckCreateNew); + connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkBinaryThresholdToolGUI::~QmitkBinaryThresholdToolGUI() { - // !!! if (m_BinaryThresholdTool.IsNotNull()) { m_BinaryThresholdTool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged); - m_BinaryThresholdTool->ThresholdingValueChanged -= mitk::MessageDelegate1( - this, &QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged); + m_BinaryThresholdTool->ThresholdingValuesChanged -= mitk::MessageDelegate2( + this, &QmitkBinaryThresholdToolGUI::OnThresholdingValuesChanged); } } void QmitkBinaryThresholdToolGUI::OnNewToolAssociated(mitk::Tool *tool) { if (m_BinaryThresholdTool.IsNotNull()) { m_BinaryThresholdTool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged); - m_BinaryThresholdTool->ThresholdingValueChanged -= mitk::MessageDelegate1( - this, &QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged); + m_BinaryThresholdTool->ThresholdingValuesChanged -= mitk::MessageDelegate2( + this, &QmitkBinaryThresholdToolGUI::OnThresholdingValuesChanged); } m_BinaryThresholdTool = dynamic_cast(tool); if (m_BinaryThresholdTool.IsNotNull()) { m_BinaryThresholdTool->IntervalBordersChanged += mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged); - m_BinaryThresholdTool->ThresholdingValueChanged += mitk::MessageDelegate1( - this, &QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged); - } -} - -void QmitkBinaryThresholdToolGUI::OnSpinnerValueChanged() -{ - if (m_BinaryThresholdTool.IsNotNull()) - { - m_ChangingSpinner = true; - double doubleVal = m_Spinner->value(); - int intVal = this->DoubleToSliderInt(doubleVal); - m_BinaryThresholdTool->SetThresholdValue(doubleVal); - if (m_ChangingSlider == false) - m_Slider->setValue(intVal); - m_ChangingSpinner = false; - } -} + m_BinaryThresholdTool->ThresholdingValuesChanged += mitk::MessageDelegate2( + this, &QmitkBinaryThresholdToolGUI::OnThresholdingValuesChanged); -void QmitkBinaryThresholdToolGUI::OnSliderValueChanged(int value) -{ - if (m_BinaryThresholdTool.IsNotNull()) - { - m_ChangingSlider = true; - double doubleVal = SliderIntToDouble(value); - if (m_ChangingSpinner == false) - m_Spinner->setValue(doubleVal); - m_ChangingSlider = false; + m_BinaryThresholdTool->SetOverwriteExistingSegmentation(true); + m_CheckProcessAll->setVisible(m_BinaryThresholdTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps()>1); } } void QmitkBinaryThresholdToolGUI::OnAcceptThresholdPreview() { - QmitkConfirmSegmentationDialog dialog; - QString segName = QString::fromStdString(m_BinaryThresholdTool->GetCurrentSegmentationName()); - - dialog.SetSegmentationName(segName); - int result = dialog.exec(); - - switch (result) + if (m_BinaryThresholdTool.IsNotNull()) { - case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: + if (m_CheckCreateNew->isChecked()) + { m_BinaryThresholdTool->SetOverwriteExistingSegmentation(false); - break; - case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: + } + else + { m_BinaryThresholdTool->SetOverwriteExistingSegmentation(true); - break; - case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: - return; - } + } + + m_BinaryThresholdTool->SetCreateAllTimeSteps(m_CheckProcessAll->isChecked()); - if (m_BinaryThresholdTool.IsNotNull()) - { this->thresholdAccepted(); m_BinaryThresholdTool->AcceptCurrentThresholdValue(); } } void QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat) { - m_isFloat = isFloat; - m_RangeMin = lower; - m_RangeMax = upper; - - m_Spinner->setRange(lower, upper); - if (!m_isFloat) + m_InternalUpdate = true; + if (!isFloat) { - m_Slider->setRange(int(lower), int(upper)); - m_Spinner->setDecimals(0); - m_Spinner->setSingleStep(1); + m_ThresholdSlider->setRange(int(lower), int(upper)); + m_ThresholdSlider->setSingleStep(1); + m_ThresholdSlider->setDecimals(0); } else { - m_Slider->setRange(0, 99); - m_Spinner->setDecimals(2); - m_Range = upper - lower; - m_Spinner->setSingleStep(m_Range / 100); + m_ThresholdSlider->setRange(lower, upper); } + m_InternalUpdate = false; } -void QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged(double current) +void QmitkBinaryThresholdToolGUI::OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType /*upper*/) { - m_Slider->setValue(DoubleToSliderInt(current)); - m_Spinner->setValue(current); + m_ThresholdSlider->setValue(lower); } -double QmitkBinaryThresholdToolGUI::SliderIntToDouble(int val) +void QmitkBinaryThresholdToolGUI::OnSliderValueChanged(double value) { - if (!m_isFloat) - { - return double(val); - } - else - { - return double(val * (m_Range) / 100 + m_RangeMin); - } -} - -int QmitkBinaryThresholdToolGUI::DoubleToSliderInt(double val) -{ - if (!m_isFloat) - { - return int(val); - } - else + if (m_BinaryThresholdTool.IsNotNull() && !m_InternalUpdate) { - int intVal = int(((val - m_RangeMin) / m_Range) * 100); - return intVal; + m_BinaryThresholdTool->SetThresholdValue(value); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.h index bab07a7824..3d1e396cd2 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUI.h @@ -1,95 +1,78 @@ /*============================================================================ 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 QmitkBinaryThresholdToolGUI_h_Included #define QmitkBinaryThresholdToolGUI_h_Included #include "QmitkToolGUI.h" #include "mitkBinaryThresholdTool.h" #include -#include +#include "ctkSliderWidget.h" +#include -class QSlider; /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::BinaryThresholdTool. This GUI shows a slider to change the tool's threshold and an OK button to accept a preview for actual thresholding. There is only a slider for INT values in QT. So, if the working image has a float/double pixeltype, we need to convert the original float intensity into a respective int value for the slider. The slider range is then between 0 and 99. If the pixeltype is INT, then we do not need any conversion. Last contributor: $Author$ */ class MITKSEGMENTATIONUI_EXPORT QmitkBinaryThresholdToolGUI : public QmitkToolGUI { Q_OBJECT public: mitkClassMacro(QmitkBinaryThresholdToolGUI, QmitkToolGUI); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat); - void OnThresholdingValueChanged(double current); + void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat); + void OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper); signals: /// \brief Emitted when threshold Accepted void thresholdAccepted(); /// \brief Emitted when threshold Canceled void thresholdCanceled(); public slots: protected slots: void OnNewToolAssociated(mitk::Tool *); void OnAcceptThresholdPreview(); - /// \brief Called when Spinner value has changed. Consider: Spinner contains DOUBLE values - void OnSpinnerValueChanged(); - - /// \brief Called when Slider value has changed. Consider: Slider contains INT values - void OnSliderValueChanged(int value); + void OnSliderValueChanged(double value); protected: QmitkBinaryThresholdToolGUI(); ~QmitkBinaryThresholdToolGUI() override; - /// \brief When Slider (int value) has changed, we need to convert it to a respective double value for the spinner - double SliderIntToDouble(int val); - - /// \brief When Spinner (double value) has changed, we need to convert it to a respective int value for the slider - int DoubleToSliderInt(double val); - - QSlider *m_Slider; - QDoubleSpinBox *m_Spinner; - - /// \brief is image float or int? - bool m_isFloat; - - double m_RangeMin; - double m_RangeMax; - double m_Range; + ctkSliderWidget* m_ThresholdSlider = nullptr; + QCheckBox* m_CheckProcessAll = nullptr; + QCheckBox* m_CheckCreateNew = nullptr; - /// \brief helper bool values to find out, which of the GUI elements has been touched by the user. - bool m_ChangingSlider, m_ChangingSpinner; + bool m_InternalUpdate = false; mitk::BinaryThresholdTool::Pointer m_BinaryThresholdTool; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.cpp index 603fb305db..8f283ae403 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.cpp @@ -1,138 +1,141 @@ /*============================================================================ 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 "QmitkBinaryThresholdULToolGUI.h" -#include "QmitkConfirmSegmentationDialog.h" #include #include #include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkBinaryThresholdULToolGUI, "") QmitkBinaryThresholdULToolGUI::QmitkBinaryThresholdULToolGUI() : QmitkToolGUI() { // create the visible widgets QBoxLayout *mainLayout = new QVBoxLayout(this); QLabel *label = new QLabel("Threshold :", this); QFont f = label->font(); f.setBold(false); label->setFont(f); mainLayout->addWidget(label); QBoxLayout *layout = new QHBoxLayout(); m_DoubleThresholdSlider = new ctkRangeWidget(); connect( m_DoubleThresholdSlider, SIGNAL(valuesChanged(double, double)), this, SLOT(OnThresholdsChanged(double, double))); layout->addWidget(m_DoubleThresholdSlider); mainLayout->addLayout(layout); m_DoubleThresholdSlider->setSingleStep(0.01); QPushButton *okButton = new QPushButton("Confirm Segmentation", this); connect(okButton, SIGNAL(clicked()), this, SLOT(OnAcceptThresholdPreview())); okButton->setFont(f); mainLayout->addWidget(okButton); + m_CheckProcessAll = new QCheckBox("Process all time steps", this); + m_CheckProcessAll->setChecked(false); + mainLayout->addWidget(m_CheckProcessAll); + + m_CheckCreateNew = new QCheckBox("Create as new segmentation", this); + m_CheckCreateNew->setChecked(false); + mainLayout->addWidget(m_CheckCreateNew); + connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkBinaryThresholdULToolGUI::~QmitkBinaryThresholdULToolGUI() { // !!! if (m_BinaryThresholdULTool.IsNotNull()) { m_BinaryThresholdULTool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdULToolGUI::OnThresholdingIntervalBordersChanged); m_BinaryThresholdULTool->ThresholdingValuesChanged -= mitk::MessageDelegate2( this, &QmitkBinaryThresholdULToolGUI::OnThresholdingValuesChanged); } } void QmitkBinaryThresholdULToolGUI::OnNewToolAssociated(mitk::Tool *tool) { if (m_BinaryThresholdULTool.IsNotNull()) { m_BinaryThresholdULTool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdULToolGUI::OnThresholdingIntervalBordersChanged); m_BinaryThresholdULTool->ThresholdingValuesChanged -= mitk::MessageDelegate2( this, &QmitkBinaryThresholdULToolGUI::OnThresholdingValuesChanged); } m_BinaryThresholdULTool = dynamic_cast(tool); if (m_BinaryThresholdULTool.IsNotNull()) { m_BinaryThresholdULTool->IntervalBordersChanged += mitk::MessageDelegate3( this, &QmitkBinaryThresholdULToolGUI::OnThresholdingIntervalBordersChanged); m_BinaryThresholdULTool->ThresholdingValuesChanged += mitk::MessageDelegate2( this, &QmitkBinaryThresholdULToolGUI::OnThresholdingValuesChanged); + + m_BinaryThresholdULTool->SetOverwriteExistingSegmentation(true); + m_CheckProcessAll->setVisible(m_BinaryThresholdULTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps() > 1); } } void QmitkBinaryThresholdULToolGUI::OnAcceptThresholdPreview() { - QmitkConfirmSegmentationDialog dialog; - QString segName = QString::fromStdString(m_BinaryThresholdULTool->GetCurrentSegmentationName()); - - dialog.SetSegmentationName(segName); - int result = dialog.exec(); - - switch (result) + if (m_BinaryThresholdULTool.IsNotNull()) { - case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: + if (m_CheckCreateNew->isChecked()) + { m_BinaryThresholdULTool->SetOverwriteExistingSegmentation(false); - break; - case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: + } + else + { m_BinaryThresholdULTool->SetOverwriteExistingSegmentation(true); - break; - case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: - return; - } + } + + m_BinaryThresholdULTool->SetCreateAllTimeSteps(m_CheckProcessAll->isChecked()); - if (m_BinaryThresholdULTool.IsNotNull()) - { m_BinaryThresholdULTool->AcceptCurrentThresholdValue(); } } void QmitkBinaryThresholdULToolGUI::OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat) { if (!isFloat) { m_DoubleThresholdSlider->setRange(int(lower), int(upper)); m_DoubleThresholdSlider->setSingleStep(1); m_DoubleThresholdSlider->setDecimals(0); } else { m_DoubleThresholdSlider->setRange(lower, upper); } } void QmitkBinaryThresholdULToolGUI::OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper) { m_DoubleThresholdSlider->setValues(lower, upper); } void QmitkBinaryThresholdULToolGUI::OnThresholdsChanged(double min, double max) { m_BinaryThresholdULTool->SetThresholdValues(min, max); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.h index 67965c9ef2..3e178be366 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdULToolGUI.h @@ -1,62 +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. ============================================================================*/ #ifndef QmitkBinaryThresholdULToolGUI_h_Included #define QmitkBinaryThresholdULToolGUI_h_Included #include "QmitkToolGUI.h" #include "ctkRangeWidget.h" +#include #include "mitkBinaryThresholdULTool.h" + #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::BinaryThresholdTool. This GUI shows a slider to change the tool's threshold and an OK button to accept a preview for actual thresholding. Last contributor: $Author$ */ class MITKSEGMENTATIONUI_EXPORT QmitkBinaryThresholdULToolGUI : public QmitkToolGUI { Q_OBJECT public: mitkClassMacro(QmitkBinaryThresholdULToolGUI, QmitkToolGUI); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat); + void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat); void OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper); signals: public slots: protected slots: void OnNewToolAssociated(mitk::Tool *); void OnAcceptThresholdPreview(); void OnThresholdsChanged(double min, double max); protected: QmitkBinaryThresholdULToolGUI(); ~QmitkBinaryThresholdULToolGUI() override; ctkRangeWidget *m_DoubleThresholdSlider; + QCheckBox* m_CheckProcessAll = nullptr; + QCheckBox* m_CheckCreateNew = nullptr; mitk::BinaryThresholdULTool::Pointer m_BinaryThresholdULTool; }; #endif