diff --git a/Modules/Multilabel/mitkLabelSet.cpp b/Modules/Multilabel/mitkLabelSet.cpp index 10d64dfffb..444139c5f8 100644 --- a/Modules/Multilabel/mitkLabelSet.cpp +++ b/Modules/Multilabel/mitkLabelSet.cpp @@ -1,373 +1,372 @@ /*============================================================================ 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 "mitkLabelSet.h" #include "mitkDICOMSegmentationPropertyHelper.h" #include mitk::LabelSet::LabelSet() : m_ActiveLabelValue(0), m_Layer(0) { m_LookupTable = mitk::LookupTable::New(); m_LookupTable->SetType(mitk::LookupTable::MULTILABEL); m_ReservedLabelValuesFunctor = nullptr; } mitk::LabelSet::~LabelSet() { m_LabelContainer.clear(); } mitk::LabelSet::LabelSet(const LabelSet &other) : itk::Object(), m_LookupTable(other.GetLookupTable()->Clone()), - m_ActiveLabelValue(other.GetActiveLabel()->GetValue()), + m_ActiveLabelValue(other.m_ActiveLabelValue), m_Layer(other.GetLayer()) { // clone Labels auto otherIt = other.IteratorConstBegin(); for (; otherIt != other.IteratorConstEnd(); ++otherIt) { m_LabelContainer[otherIt->first] = otherIt->second->Clone(); itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &LabelSet::OnLabelModified); m_LabelContainer[otherIt->first]->AddObserver(itk::ModifiedEvent(), command); } m_ReservedLabelValuesFunctor = other.m_ReservedLabelValuesFunctor; } std::vector mitk::LabelSet::GetUsedLabelValues() const { std::vector result = { 0 }; if (m_ReservedLabelValuesFunctor != nullptr) { result = m_ReservedLabelValuesFunctor(); } else { - for (auto [value, label] : this->m_LabelContainer) { result.emplace_back(value); } } return result; } void mitk::LabelSet::OnLabelModified(const Object* sender, const itk::EventObject&) { auto label = dynamic_cast(sender); if (nullptr == label) mitkThrow() << "LabelSet is in wrong state. LabelModified event is not send by a label instance."; ModifyLabelEvent.Send(label->GetValue()); Superclass::Modified(); } mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstEnd() const { return m_LabelContainer.end(); } mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstBegin() const { return m_LabelContainer.begin(); } mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorEnd() { return m_LabelContainer.end(); } mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorBegin() { return m_LabelContainer.begin(); } unsigned int mitk::LabelSet::GetNumberOfLabels() const { return m_LabelContainer.size(); } void mitk::LabelSet::SetLayer(unsigned int layer) { m_Layer = layer; Modified(); } void mitk::LabelSet::SetActiveLabel(PixelType pixelValue) { m_ActiveLabelValue = pixelValue; ActiveLabelEvent.Send(pixelValue); Modified(); } bool mitk::LabelSet::ExistLabel(PixelType pixelValue) { return m_LabelContainer.count(pixelValue) > 0 ? true : false; } mitk::Label* mitk::LabelSet::AddLabel(mitk::Label *label, bool addAsClone) { unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1; if (m_LabelContainer.size() >= max_size) return nullptr; mitk::Label::Pointer newLabel = addAsClone ? label->Clone() : Label::Pointer(label); // TODO use layer of label parameter newLabel->SetLayer(m_Layer); PixelType pixelValue = newLabel->GetValue(); auto usedValues = this->GetUsedLabelValues(); auto finding = std::find(usedValues.begin(), usedValues.end(), pixelValue); if (!usedValues.empty() && usedValues.end() != finding) { pixelValue = usedValues.back()+1; MITK_DEBUG << "LabelSet label collision. Tried to add a label with a value already in use. Value will be adapted. Old value: " << newLabel->GetValue() << "; new value: " << pixelValue; newLabel->SetValue(pixelValue); } // new map entry m_LabelContainer[pixelValue] = newLabel; UpdateLookupTable(pixelValue); // add DICOM information of the label DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel); itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &LabelSet::OnLabelModified); newLabel->AddObserver(itk::ModifiedEvent(), command); AddLabelEvent.Send(newLabel->GetValue()); SetActiveLabel(newLabel->GetValue()); Modified(); return newLabel; } mitk::Label* mitk::LabelSet::AddLabel(const std::string &name, const mitk::Color &color) { mitk::Label::Pointer newLabel = mitk::Label::New(); newLabel->SetName(name); newLabel->SetColor(color); return AddLabel(newLabel); } void mitk::LabelSet::RenameLabel(PixelType pixelValue, const std::string &name, const mitk::Color &color) { mitk::Label *label = GetLabel(pixelValue); label->SetName(name); label->SetColor(color); // change DICOM information of the label DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label); } void mitk::LabelSet::SetLookupTable(mitk::LookupTable *lut) { m_LookupTable = lut; Modified(); } void mitk::LabelSet::PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const { } void mitk::LabelSet::RemoveLabel(PixelType pixelValue) { if (LabelSetImage::UnlabeledLabelValue == pixelValue) return; auto it = m_LabelContainer.rbegin(); PixelType nextActivePixelValue = it->first; for (; it != m_LabelContainer.rend(); ++it) { if (it->first == pixelValue) { it->second->RemoveAllObservers(); m_LabelContainer.erase(pixelValue); break; } nextActivePixelValue = it->first; } if (m_ActiveLabelValue == pixelValue) { if (ExistLabel(nextActivePixelValue)) SetActiveLabel(nextActivePixelValue); else SetActiveLabel(m_LabelContainer.rbegin()->first); } RemoveLabelEvent.Send(pixelValue); Modified(); } void mitk::LabelSet::RemoveAllLabels() { auto _it = IteratorBegin(); for (; _it != IteratorConstEnd();) { auto labelValue = _it->first; m_LabelContainer.erase(_it++); RemoveLabelEvent.Send(labelValue); } AllLabelsModifiedEvent.Send(); } void mitk::LabelSet::SetNextActiveLabel() { auto it = m_LabelContainer.find(m_ActiveLabelValue); if (it != m_LabelContainer.end()) ++it; if (it == m_LabelContainer.end()) { it = m_LabelContainer.begin(); if (m_LabelContainer.size() > 1) ++it; // ...skip background label! } SetActiveLabel(it->first); } void mitk::LabelSet::SetAllLabelsLocked(bool value) { auto _end = m_LabelContainer.end(); auto _it = m_LabelContainer.begin(); for (; _it != _end; ++_it) _it->second->SetLocked(value); AllLabelsModifiedEvent.Send(); Modified(); } void mitk::LabelSet::SetAllLabelsVisible(bool value) { auto _end = m_LabelContainer.end(); auto _it = m_LabelContainer.begin(); for (; _it != _end; ++_it) { _it->second->SetVisible(value); UpdateLookupTable(_it->first); } AllLabelsModifiedEvent.Send(); Modified(); } void mitk::LabelSet::UpdateLookupTable(PixelType pixelValue) { const mitk::Color &color = GetLabel(pixelValue)->GetColor(); double rgba[4]; m_LookupTable->GetTableValue(static_cast(pixelValue), rgba); rgba[0] = color.GetRed(); rgba[1] = color.GetGreen(); rgba[2] = color.GetBlue(); if (GetLabel(pixelValue)->GetVisible()) rgba[3] = GetLabel(pixelValue)->GetOpacity(); else rgba[3] = 0.0; m_LookupTable->SetTableValue(static_cast(pixelValue), rgba); } mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) { if (m_LabelContainer.find(pixelValue) == m_LabelContainer.end()) return nullptr; return m_LabelContainer[pixelValue]; } const mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) const { auto it = m_LabelContainer.find(pixelValue); if (it == m_LabelContainer.end()) return nullptr; return it->second.GetPointer(); } bool mitk::Equal(const mitk::LabelSet &leftHandSide, const mitk::LabelSet &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // LabelSetmembers MITK_INFO(verbose) << "--- LabelSet Equal ---"; // m_LookupTable; const mitk::LookupTable *lhsLUT = leftHandSide.GetLookupTable(); const mitk::LookupTable *rhsLUT = rightHandSide.GetLookupTable(); returnValue = *lhsLUT == *rhsLUT; if (!returnValue) { MITK_INFO(verbose) << "Lookup tabels not equal."; return returnValue; ; } // m_ActiveLabel; returnValue = mitk::Equal(*leftHandSide.GetActiveLabel(), *rightHandSide.GetActiveLabel(), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Active label not equal."; return returnValue; ; } // m_Layer; returnValue = leftHandSide.GetLayer() == rightHandSide.GetLayer(); if (!returnValue) { MITK_INFO(verbose) << "Layer index not equal."; return returnValue; ; } // container size; returnValue = leftHandSide.GetNumberOfLabels() == rightHandSide.GetNumberOfLabels(); if (!returnValue) { MITK_INFO(verbose) << "Number of labels not equal."; return returnValue; ; } // Label container (map) // m_LabelContainer; auto lhsit = leftHandSide.IteratorConstBegin(); auto rhsit = rightHandSide.IteratorConstBegin(); for (; lhsit != leftHandSide.IteratorConstEnd(); ++lhsit, ++rhsit) { returnValue = rhsit->first == lhsit->first; if (!returnValue) { MITK_INFO(verbose) << "Label in label container not equal."; return returnValue; ; } returnValue = mitk::Equal(*(rhsit->second), *(lhsit->second), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Label in label container not equal."; return returnValue; ; } } return returnValue; } diff --git a/Modules/Multilabel/mitkLabelSet.h b/Modules/Multilabel/mitkLabelSet.h index 9c35dcb3a0..d2bf809a0f 100644 --- a/Modules/Multilabel/mitkLabelSet.h +++ b/Modules/Multilabel/mitkLabelSet.h @@ -1,247 +1,255 @@ /*============================================================================ 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 mitkLabelSet_h #define mitkLabelSet_h #include "MitkMultilabelExports.h" #include #include #include #include #include #include namespace mitk { // // Documentation // @brief LabelSet containing the labels corresponding to a segmentation session. // @ingroup Data // class MITKMULTILABEL_EXPORT LabelSet : public itk::Object { public: mitkClassMacroItkParent(LabelSet, itk::Object); itkNewMacro(Self); typedef mitk::Label::PixelType PixelType; using LabelValueType = mitk::Label::PixelType; typedef std::map LabelContainerType; typedef LabelContainerType::const_iterator LabelContainerConstIteratorType; typedef LabelContainerType::iterator LabelContainerIteratorType; /** * \brief AddLabelEvent is emitted whenever a new label has been added to the LabelSet. * * The registered method will be called with the label value of the added label. * Observers should register to this event by calling myLabelSet->AddLabelEvent.AddListener(myObject, * MyObject::MyMethod). * After registering, myObject->MyMethod() will be called every time a new label has been added to the LabelSet. * Observers should unregister by calling myLabelSet->AddLabelEvent.RemoveListener(myObject, MyObject::MyMethod). * * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for * a Message1 object which is thread safe */ Message1 AddLabelEvent; /** * \brief RemoveLabelEvent is emitted whenever a new label has been removed from the LabelSet. * * Observers should register to this event by calling myLabelSet->RemoveLabelEvent.AddListener(myObject, * MyObject::MyMethod). * After registering, myObject->MyMethod() will be called every time a new label has been removed from the LabelSet. * Observers should unregister by calling myLabelSet->RemoveLabelEvent.RemoveListener(myObject, MyObject::MyMethod). * * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for * a Message object which is thread safe */ Message1 RemoveLabelEvent; /** * \brief ModifyLabelEvent is emitted whenever a label has been modified from the LabelSet. * * Observers should register to this event by calling myLabelSet->ModifyLabelEvent.AddListener(myObject, * MyObject::MyMethod). * After registering, myObject->MyMethod() will be called every time a new label has been removed from the LabelSet. * Observers should unregister by calling myLabelSet->ModifyLabelEvent.RemoveListener(myObject, MyObject::MyMethod). * * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for * a Message object which is thread safe */ Message1 ModifyLabelEvent; /** * \brief ActiveLabelEvent is emitted whenever a label has been set as active in the LabelSet. */ Message1 ActiveLabelEvent; /** * \brief AllLabelsModifiedEvent is emitted whenever a new label has been removed from the LabelSet. * * Observers should register to this event by calling myLabelSet->AllLabelsModifiedEvent.AddListener(myObject, * MyObject::MyMethod). * After registering, myObject->MyMethod() will be called every time a new label has been removed from the LabelSet. * Observers should unregister by calling myLabelSet->AllLabelsModifiedEvent.RemoveListener(myObject, * MyObject::MyMethod). * * member variable is not needed to be locked in multi-threaded scenarios since the LabelSetEvent is a typedef for * a Message object which is thread safe */ Message<> AllLabelsModifiedEvent; /** \brief Returns a const iterator poiting to the begining of the container. */ LabelContainerConstIteratorType IteratorConstBegin() const; /** \brief Returns a const iterator pointing to the end of the container. */ LabelContainerConstIteratorType IteratorConstEnd() const; /** \brief Returns a iterator poiting to the begining of the container. */ LabelContainerIteratorType IteratorBegin(); /** \brief Returns a iterator pointing to the end of the container. */ LabelContainerIteratorType IteratorEnd(); /** \brief * Recall itk::Object::Modified event from a label and send a ModifyLabelEvent */ void OnLabelModified(const Object*, const itk::EventObject&); /** \brief */ void SetLayer(unsigned int); /** \brief */ void SetActiveLabel(PixelType); /** \brief */ void RemoveLabel(PixelType); /** \brief */ bool ExistLabel(PixelType); /** \brief */ mitk::Label* AddLabel(mitk::Label *label, bool addAsClone = true); /** \brief */ mitk::Label* AddLabel(const std::string &name, const Color &color); /** \brief */ void RenameLabel(PixelType, const std::string &, const Color &); /** \brief */ unsigned int GetNumberOfLabels() const; /** \brief */ void SetAllLabelsVisible(bool); /** \brief */ void SetAllLabelsLocked(bool); /** \brief */ void RemoveAllLabels(); void SetNextActiveLabel(); /** \brief */ Label *GetActiveLabel() { return GetLabel(m_ActiveLabelValue); } /** \brief */ const Label *GetActiveLabel() const { return GetLabel(m_ActiveLabelValue); } /** \brief */ Label *GetLabel(PixelType pixelValue); /** \brief */ const Label *GetLabel(PixelType pixelValue) const; itkGetMacro(Layer, int); itkGetConstMacro(Layer, int); itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable); /** \brief */ void SetLookupTable(LookupTable *lut); /** \brief */ void UpdateLookupTable(PixelType pixelValue); using ReservedLabelValuesFunctor = std::function()>; ReservedLabelValuesFunctor m_ReservedLabelValuesFunctor; + std::vector GetUsedLabelValues() const; + protected: LabelSet(); LabelSet(const LabelSet &); mitkCloneMacro(Self); ~LabelSet() override; - std::vector GetUsedLabelValues() const; - void PrintSelf(std::ostream &os, itk::Indent indent) const override; LabelContainerType m_LabelContainer; LookupTable::Pointer m_LookupTable; PixelType m_ActiveLabelValue; unsigned int m_Layer; }; /** * @brief Equal A function comparing two label sets for beeing equal in data * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - LabelSetmembers * - Label container (map) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSet &leftHandSide, const mitk::LabelSet &rightHandSide, ScalarType eps, bool verbose); + /** + * Methods takes a label set and generates a new label set with the same labels but updated labels values according to + * the passed labelMapping. + * @pre sourceLabelSet is valid + */ + MITKMULTILABEL_EXPORT LabelSet::Pointer GenerateLabelSetWithMappedValues(const LabelSet* sourceLabelSet, + std::vector > labelMapping = { {1,1} }); + } // namespace mitk #endif