diff --git a/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp index 5d98f23651..928c0507f4 100644 --- a/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp @@ -1,93 +1,93 @@ /*============================================================================ 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 "mitkEraseRegionTool.h" #include "mitkEraseRegionTool.xpm" #include #include #include // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, EraseRegionTool, "Erase tool"); } const char **mitk::EraseRegionTool::GetXPM() const { return mitkEraseRegionTool_xpm; } us::ModuleResource mitk::EraseRegionTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Erase_48x48.png"); return resource; } us::ModuleResource mitk::EraseRegionTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Erase_Cursor_32x32.png"); return resource; } const char *mitk::EraseRegionTool::GetName() const { return "Erase"; } template void DoFillImage(itk::Image* image) { image->FillBuffer(1); }; -mitk::Image::Pointer mitk::EraseRegionTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint) const +mitk::Image::Pointer mitk::EraseRegionTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const { auto labelSetImage = dynamic_cast(this->GetWorkingData()); itk::Index<2> seedIndex; workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex); using AccessorType = ImagePixelReadAccessor; AccessorType accessor(workingSlice); - + seedLabelValue = accessor.GetPixelByIndex(seedIndex); Image::Pointer fillImage; - if (accessor.GetPixelByIndex(seedIndex) == labelSetImage->GetExteriorLabel()->GetValue()) + if ( seedLabelValue == labelSetImage->GetExteriorLabel()->GetValue()) { //clicked on background remove everything which is not locked. fillImage = workingSlice->Clone(); AccessByItk(fillImage, DoFillImage); } else { - fillImage = Superclass::GenerateFillImage(workingSlice, seedPoint); + fillImage = Superclass::GenerateFillImage(workingSlice, seedPoint, seedLabelValue); } return fillImage; } void mitk::EraseRegionTool::PrepareFilling(const Image* /*workingSlice*/, Point3D /*seedPoint*/) { auto labelSetImage = dynamic_cast(this->GetWorkingData()); if (nullptr != labelSetImage) { - m_FillValue = labelSetImage->GetExteriorLabel()->GetValue(); + m_FillLabelValue = labelSetImage->GetExteriorLabel()->GetValue(); } }; diff --git a/Modules/Segmentation/Interactions/mitkEraseRegionTool.h b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h index 7244ebcb1b..abeeeb476b 100644 --- a/Modules/Segmentation/Interactions/mitkEraseRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h @@ -1,64 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkEraseRegionTool_h_Included #define mitkEraseRegionTool_h_Included #include "mitkFillRegionBaseTool.h" #include namespace us { class ModuleResource; } namespace mitk { /** \brief Erase the inside of a contour by filling the inside of a contour with the background pixel value. \sa SetRegionTool \ingroup Interactions Finds the outer contour of a shape in 2D (possibly including single patches) and sets all the pixels inside to the background pixel value (erasing a segmentation). If clicked on the background, the outer contour might contain the whole image and thus fill the whole image with the background pixel value. \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT EraseRegionTool : public FillRegionBaseTool { public: mitkClassMacro(EraseRegionTool, FillRegionBaseTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; protected: EraseRegionTool() = default; // purposely hidden ~EraseRegionTool() = default; - Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint) const override; + Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const override; void PrepareFilling(const Image* workingSlice, Point3D seedPoint) override; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp index 7809eb3c9c..525816556b 100644 --- a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp @@ -1,137 +1,145 @@ /*============================================================================ 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 "mitkFillRegionBaseTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkDataStorage.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkRenderingManager.h" #include mitk::FillRegionBaseTool::FillRegionBaseTool() : SegTool2D("MouseReleaseOnly") { } mitk::FillRegionBaseTool::~FillRegionBaseTool() { } void mitk::FillRegionBaseTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("Release", OnClick); } template void DoITKRegionGrowing(const itk::Image* oldSegImage, mitk::Image::Pointer& filledRegionImage, itk::Index seedIndex, mitk::Label::PixelType& seedLabel ) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; seedLabel = oldSegImage->GetPixel(seedIndex); typename OutputImageType::Pointer itkResultImage; filledRegionImage = nullptr; try { typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); regionGrower->SetInput(oldSegImage); regionGrower->SetReplaceValue(1); regionGrower->AddSeed(seedIndex); regionGrower->SetLower(seedLabel); regionGrower->SetUpper(seedLabel); regionGrower->Update(); itkResultImage = regionGrower->GetOutput(); } catch (const itk::ExceptionObject&) { return; // can't work } catch (...) { return; } mitk::CastToMitkImage(itkResultImage, filledRegionImage); } -#include "mitkIOUtil.h" - void mitk::FillRegionBaseTool::OnClick(StateMachineAction*, InteractionEvent* interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; auto labelSetImage = dynamic_cast(this->GetWorkingData()); if (nullptr == labelSetImage) { return; } if (!IsPositionEventInsideImageRegion(positionEvent, labelSetImage)) { return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); auto workingSlice = this->GetAffectedWorkingSlice(positionEvent); auto click = positionEvent->GetPositionInWorld(); - auto fillImage = this->GenerateFillImage(workingSlice, click); + m_SeedLabelValue = 0; + auto fillImage = this->GenerateFillImage(workingSlice, click, m_SeedLabelValue); + + if (fillImage.IsNull()) + { + return; //nothing to fill; + } + + auto label = labelSetImage->GetLabel(m_SeedLabelValue, labelSetImage->GetActiveLayer()); + + if (label->GetLocked() && label->GetValue()!=labelSetImage->GetActiveLabel()->GetValue()) + { + ErrorMessage.Send("Label of selected region is locked. Tool operation has no effect."); + return; + } + this->PrepareFilling(workingSlice, click); - TransferLabelContent(fillImage, workingSlice, labelSetImage->GetActiveLabelSet(), 0, labelSetImage->GetExteriorLabel()->GetValue(), false, { {1, m_FillValue} }, m_MergeStyle); + //as fill region tools should always allow to manipulate active label + //(that is what the user expects/knows when using tools so far: + //the active label can always be changed even if locked) + //we realize that by cloning the relevant label set and changing the lock state + //this fillLabelSet is used for the transfer. + auto fillLabelSet = labelSetImage->GetActiveLabelSet()->Clone(); + fillLabelSet->GetLabel(labelSetImage->GetActiveLabel()->GetValue())->SetLocked(false); + + TransferLabelContent(fillImage, workingSlice, fillLabelSet, 0, labelSetImage->GetExteriorLabel()->GetValue(), false, { {1, m_FillLabelValue} }, m_MergeStyle); this->WriteBackSegmentationResult(positionEvent, workingSlice); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } -mitk::Image::Pointer mitk::FillRegionBaseTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint) const +mitk::Image::Pointer mitk::FillRegionBaseTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const { itk::Index<2> seedIndex; workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex); Image::Pointer fillImage; - Label::PixelType seedLabelValue; AccessFixedDimensionByItk_n(workingSlice, DoITKRegionGrowing, 2, (fillImage, seedIndex, seedLabelValue)); - auto labelSetImage = dynamic_cast(this->GetWorkingData()); - if (nullptr != labelSetImage) - { - auto label = labelSetImage->GetLabel(seedLabelValue, labelSetImage->GetActiveLayer()); - - if (label->GetLocked()) - { - ErrorMessage.Send("Label of selected region is locked. Tool operation may not have an effect."); - } - } - return fillImage; } - diff --git a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h index 722cfde266..8235310900 100644 --- a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h +++ b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h @@ -1,74 +1,83 @@ /*============================================================================ 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 mitkFillRegionBaseTool_h_Included #define mitkFillRegionBaseTool_h_Included #include "mitkCommon.h" #include "mitkContourModelUtils.h" #include "mitkContourUtils.h" //TODO remove legacy support #include "mitkImage.h" #include "mitkSegTool2D.h" #include #include "mitkDataNode.h" #include "mitkImageCast.h" namespace mitk { /** \brief Base class for tools that fill a connected region of a 2D slice \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. $Author: nolden $ */ class MITKSEGMENTATION_EXPORT FillRegionBaseTool : public SegTool2D { public: mitkClassMacro(FillRegionBaseTool, SegTool2D); protected: FillRegionBaseTool(); // purposely hidden ~FillRegionBaseTool() override; void ConnectActionsAndFunctions() override; /// \brief Add a control point and finish current segment. virtual void OnClick(StateMachineAction*, InteractionEvent* interactionEvent); /** Function that generates the mask image that indicates which pixels should be filled. * Caller of this function assumes that all pixels that should be filled have the value 1. * Pixels that should stay untouched should have the value 0. * The default implementation marks the connected reagion around seedPoint, that has * the same pixel value/label like the seedPoint. - * You may reimplement this function to change the strategy to determin the fill region.*/ - virtual Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint) const; + * You may reimplement this function to change the strategy to determin the fill region. + * @param workingSlice part of the segmentation image that should be used to determin the fill image. + * @param seedPoint The world coordinate position where the user has cliced. + * @param [out] seedLabelValue The function should return the label value that should be assumed + * as clicked on, given the seedPoint. + * @return Return the image maske that indicates which pixels should be filled. Returning + * a null pointer indicates that there is nothing to fill. + */ + virtual Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const; /** Function that is called by OnClick before the filling is executed. If you want to do special - * preperation (e.g. change m_FillValue, you can overwrite this function. */ + * preperation (e.g. change m_FillLabelValue, you can overwrite this function. */ virtual void PrepareFilling(const Image* workingSlice, Point3D seedPoint) = 0; - Label::PixelType m_FillValue = 0; + Label::PixelType m_FillLabelValue = 0; + Label::PixelType m_SeedLabelValue = 0; + MultiLabelSegmentation::MergeStyle m_MergeStyle = MultiLabelSegmentation::MergeStyle::Replace; private: }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkFillRegionTool.cpp b/Modules/Segmentation/Interactions/mitkFillRegionTool.cpp index 2fba00dbac..cdde1d0b95 100644 --- a/Modules/Segmentation/Interactions/mitkFillRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFillRegionTool.cpp @@ -1,59 +1,59 @@ /*============================================================================ 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 "mitkFillRegionTool.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FillRegionTool, "Fill tool"); } const char **mitk::FillRegionTool::GetXPM() const { return nullptr; } us::ModuleResource mitk::FillRegionTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Fill_48x48.png"); return resource; } us::ModuleResource mitk::FillRegionTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Fill_Cursor_32x32.png"); return resource; } const char *mitk::FillRegionTool::GetName() const { return "Fill"; } void mitk::FillRegionTool::PrepareFilling(const Image* /*workingSlice*/, Point3D /*seedPoint*/) { auto labelSetImage = dynamic_cast(this->GetWorkingData()); if (nullptr == labelSetImage) mitkThrow() << "Invalid state of FillRegionTool. Working image is not of correct type."; - m_FillValue = labelSetImage->GetActiveLabel()->GetValue(); + m_FillLabelValue = labelSetImage->GetActiveLabel()->GetValue(); m_MergeStyle = MultiLabelSegmentation::MergeStyle::Merge; };