diff --git a/Modules/Multilabel/mitkLabelSetImage.h b/Modules/Multilabel/mitkLabelSetImage.h index 34dd563e7b..ad75094349 100644 --- a/Modules/Multilabel/mitkLabelSetImage.h +++ b/Modules/Multilabel/mitkLabelSetImage.h @@ -1,680 +1,667 @@ /*============================================================================ 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 mitkLabelSetImage_h #define mitkLabelSetImage_h #include #include #include #include #include #include #include #include namespace mitk { /** @brief LabelSetImage class for handling labels and layers in a segmentation session. * * Events that are potentially send by the class in regard to groups or labels: * - LabelAddedEvent is emitted whenever a new label has been added. * - LabelModifiedEvent is emitted whenever a label has been modified. * - LabelRemovedEvent is emitted whenever a label has been removed. * - LabelsChangedEvent is emitted when labels are changed (added, removed, modified). In difference to the other label events LabelsChanged is send only *one time* after the modification of the * MultiLableImage instance is finished. So e.g. even if 4 labels are changed by a merge operation, this event will * only be sent once (compared to LabelRemoved or LabelModified). * - GroupAddedEvent is emitted whenever a new group has been added. * - GroupModifiedEvent is emitted whenever a group has been modified. * - GroupRemovedEvent is emitted whenever a label has been removed. * * @ingroup Data */ class MITKMULTILABEL_EXPORT LabelSetImage : public Image { public: /** * \brief BeforeChangeLayerEvent (e.g. used for GUI integration) * As soon as active labelset should be changed, the signal emits. * Emitted by SetActiveLayer(int layer); */ Message<> BeforeChangeLayerEvent; /** * \brief AfterchangeLayerEvent (e.g. used for GUI integration) * As soon as active labelset was changed, the signal emits. * Emitted by SetActiveLayer(int layer); */ Message<> AfterChangeLayerEvent; /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// // FUTURE MultiLabelSegmentation: // Section that already contains declarations used in the new class. // So this part of the interface will stay after refactoring towards // the new MultiLabelSegmentation class (see T28524). This section was introduced // because some of the planned features are already urgently needed. /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// mitkClassMacro(LabelSetImage, Image); itkNewMacro(Self); typedef mitk::Label::PixelType PixelType; using GroupIndexType = std::size_t; using LabelValueType = mitk::Label::PixelType; using ConstLabelVectorType = ConstLabelVector; using LabelVectorType = LabelVector; using LabelValueVectorType = std::vector; const static LabelValueType UNLABELED_VALUE; /** \brief Adds a label instance to a group of the multi label image. * @remark By default, if the pixel value of the label is already used in the image, the label * will get a new none conflicting value assigned. This can be controlled by correctLabelValue. * @param label Instance of an label that should be added or used as template * @param groupID The id of the group the label should be added to. * @param addAsClone Flag that controls, if the passed instance should be added (false; the image will then take ownership, * be aware that e.g. event observers will be added) * a clone of the instance (true). * @param correctLabelValue Flag that controls, if the value of the passed label should be correct, if this value is already used in * the multi label image. True: Conflicting values will be corrected, be assigning a none conflicting value. False: If the value is conflicting * an exception will be thrown. * @return Instance of the label as it was added to the label set. * @pre label must point to a valid instance. * @pre If correctLabelValue==false, label value must be non conflicting. * @pre groupID must indicate an existing group. */ mitk::Label* AddLabel(mitk::Label* label, GroupIndexType groupID, bool addAsClone = true, bool correctLabelValue = true); /** \brief Adds a new label to a group of the image by providing name and color. * @param name (Class) name of the label instance that should be added. * @param color Color of the new label instance. * @param groupID The id of the group the label should be added to. * @return Instance of the label as it was added to the label set. * @pre groupID must indicate an existing group. */ mitk::Label* AddLabel(const std::string& name, const Color& color, GroupIndexType groupID); /** \brief allows to adapt name and color of a certain label * @param labelValue Value of the label that should be changed * @param name New name for the label * @param color New color for the label * @pre Indicated label value must exist. */ void RenameLabel(LabelValueType labelValue, const std::string& name, const Color& color); /** * @brief Removes the label with the given value. * The label is removed from the labelset and * the pixel with the value of the label are set to UNLABELED_VALUE. * @param labelValue the pixel value of the label to be removed. If the label is unknown, * the method will return without doing anything. */ void RemoveLabel(LabelValueType labelValue); /** * @brief Removes labels from the mitk::MultiLabelSegmentation. * The label is removed from the labelset and * the pixel with the value of the label are set to UNLABELED_VALUE. * If a label value does not exist, it will be ignored. * @param vectorOfLabelPixelValues a list of labels to be removed */ void RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues); /** * @brief Erases the label with the given value from the labelset image. * The label itself will not be erased from the respective mitk::LabelSet. In order to * remove the label itself use mitk::LabelSetImage::RemoveLabels() * @param labelValue the pixel value of the label that will be erased from the labelset image * @pre labelValue must exist. */ void EraseLabel(LabelValueType labelValue); /** * @brief Erases a list of labels with the given values from the labelset image. * @param labelValues the list of pixel values of the labels * that will be erased from the labelset image * @pre label values must exist */ void EraseLabels(const LabelValueVectorType& labelValues); /** * @brief Removes a whole group including all its labels. * @remark with removing a group all groups with greater index will be re-indexed to * close the gap. Hence externally stored spatial group indices may become invalid. * @param group Group index of the spatial group that should be removed. If the spatial group does not exist, an * exception will be raised. * @pre group index must be valid. */ void RemoveGroup(GroupIndexType group); /** \brief Returns true if the value exists in the MultiLabelSegmentation instance*/ bool ExistLabel(LabelValueType value) const; /** * @brief Checks if a label belongs in a certain spatial group * @param value the label value * @param groupIndex Index of the spacial group which should be checked for the label * @return true if the label exists otherwise false */ bool ExistLabel(LabelValueType value, GroupIndexType groupIndex) const; /** * @brief Returns true if the spatial group exists in the MultiLabelSegmentation instance. * * @param index Group index of the group that should be checked for existence. */ bool ExistGroup(GroupIndexType index) const; /** Returns the group id of the based label value. * @pre label value must exists. */ GroupIndexType GetGroupIndexOfLabel(LabelValueType value) const; /** * @brief Returns the mitk::Label with the given value. * @param value the pixel value of the label * @return the label instance if defined in the segmentation, otherwise nullptr. */ const mitk::Label* GetLabel(LabelValueType value) const; mitk::Label* GetLabel(LabelValueType value); /** Returns a vector with pointers to all labels currently defined in the MultiLabelSegmentation instance.*/ const ConstLabelVectorType GetLabels() const; const LabelVectorType GetLabels(); /** Returns a vector of all label values currently defined in the MultiLabelSegmentation instance.*/ const LabelValueVectorType GetAllLabelValues() const; /** @brief Returns a vector with pointers to all labels in the MultiLabelSegmentation indicated * by the passed label value vector. * @param labelValues Vector of values of labels that should be returned. - * @ignoreMissing If true(Default) unknown labels Will be skipped in the result. If false, + * @param ignoreMissing If true (default), unknown labels Will be skipped in the result. If false, * an exception will be raised, if a label is requested. - instance.*/ + */ const LabelVectorType GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = true); /** @brief Returns a vector with const pointers to all labels in the MultiLabelSegmentation indicated * by the passed label value vector. * For details see GetLabelsByValue(); */ const ConstLabelVectorType GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = false) const; - /** Helper function that can be used to extract a vector of label values of a vector of label instance pointers. - @overload.*/ + /** Helper function that can be used to extract a vector of label values of a vector of label instance pointers.*/ static LabelValueVectorType ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels); /** Helper function that can be used to extract a vector of label values are vector of label instances.*/ static LabelValueVectorType ExtractLabelValuesFromLabelVector(const LabelVectorType& labels); /** Helper function that converts a given vector of label instance pointers into a vector of const pointers.*/ static ConstLabelVectorType ConvertLabelVectorConst(const LabelVectorType& labels); /** * @brief Returns a vector of all label values located on the specified group. * @param index the index of the group for which the vector of labels should be retrieved. * If an invalid index is passed an exception will be raised. * @return the respective vector of label values. * @pre group index must exist. */ const LabelValueVectorType GetLabelValuesByGroup(GroupIndexType index) const; /** * @brief Returns a vector of all label values located on the specified group having a certain name. * @param index the index of the group for which the vector of labels should be retrieved. * If an invalid index is passed an exception will be raised. * @param name Name of the label instances one is looking for. * @return the respective vector of label values. * @pre group index must exist. */ const LabelValueVectorType GetLabelValuesByName(GroupIndexType index, const std::string_view name) const; /** * Returns a vector with (class) names of all label instances used in the segmentation (over all groups) */ std::vector GetLabelClassNames() const; /** * Returns a vector with (class) names of all label instances present in a certain group. * @param index ID of the group, for which the label class names should be returned * @pre Indicated group must exist. */ std::vector GetLabelClassNamesByGroup(GroupIndexType index) const; /** Helper that returns an unused label value, that could be used e.g. if one wants to define a label externally * before adding it. * @return A label value currently not in use. * @remark is no unused label value can be provided an exception will be thrown.*/ LabelValueType GetUnusedLabelValue() const; itkGetConstMacro(UnlabeledLabelLock, bool); itkSetMacro(UnlabeledLabelLock, bool); itkBooleanMacro(UnlabeledLabelLock); /** Set the visibility of all label instances accordingly to the passed state. */ void SetAllLabelsVisible(bool visible); /** Set the visibility of all label instances in a group accordingly to the passed state. * @pre The specified group must exist. */ void SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible); /** Set the visibility of all label instances In a group with a given class name * accordingly to the passed state. * @pre The specified group must exist. */ void SetAllLabelsVisibleByName(GroupIndexType group, const std::string_view name, bool visible); /** Returns the lock state of the label (including UnlabeledLabel value). @pre Requested label does exist.*/ bool IsLabelLocked(LabelValueType value) const; /** Set the lock state of all label instances accordingly to the passed state. */ void SetAllLabelsLocked(bool locked); /** Set the lock state of all label instances in a group accordingly to the passed state. * @pre The specified group must exist. */ void SetAllLabelsLockedByGroup(GroupIndexType group, bool locked); /** Set the lock state of all label instances In a group with a given class name * accordingly to the passed state. * @pre The specified group must exist. */ void SetAllLabelsLockedByName(GroupIndexType group, const std::string_view name, bool locked); /** * \brief Replaces the labels of a group with a given vector of labels. * * @remark The passed label instances will be cloned before added to ensure clear ownership * of the new labels. * @remark The pixel content of the old labels will not be removed. * @param groupID The index of the group that should have its labels replaced * @param newLabels The vector of new labels * @pre Group that should be replaced must exist. * @pre new label values must not be used in other groups. */ void ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& newLabels); - /**@overload for none-const label vectors. */ void ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& newLabels); /** Returns the pointer to the image that contains the labeling of the indicate group. *@pre groupID must reference an existing group.*/ mitk::Image* GetGroupImage(GroupIndexType groupID); /** Returns the pointer to the image that contains the labeling of the indicate group. *@pre groupID must reference an existing group.*/ const mitk::Image* GetGroupImage(GroupIndexType groupID) const; itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable); void SetLookupTable(LookupTable* lut); /** Updates the lookup table for a label indicated by the passed label value using the color of the label. * @pre labelValue must exist. */ void UpdateLookupTable(PixelType pixelValue); protected: void OnLabelModified(const Object* sender, const itk::EventObject&); /** Helper to ensure that the maps are correctly populated for a new label instance.*/ void AddLabelToMap(LabelValueType labelValue, Label* label, GroupIndexType groupID); void RemoveLabelFromMap(LabelValueType labelValue); /** Helper to ensure label events are correctly connected and lookup table is updated for a new label instance.*/ void RegisterLabel(Label* label); /** Helper to ensure label events are unregistered.*/ void ReleaseLabel(Label* label); /** Helper class used internally to apply lambda functions to the labels specified by the passed label value vector. */ void ApplyToLabels(const LabelValueVectorType& values, std::function&& lambda); /** Helper class used internally to for visiting the labels specified by the passed label value vector * with the lambda function. */ void VisitLabels(const LabelValueVectorType& values, std::function&& lambda) const; LabelValueType m_ActiveLabelValue; private: using LabelMapType = std::map; /** Dictionary that holds all known labels (label value is the key).*/ LabelMapType m_LabelMap; using GroupNameVectorType = std::vector; /** Vector storing the names of all groups. If a group has no user name defined, string is empty.*/ GroupNameVectorType m_Groups; /**This type is internally used to track which label is currently * associated with which layer.*/ using GroupToLabelMapType = std::vector; /* Dictionary that maps between group id (key) and label values in the group (vector of label value).*/ GroupToLabelMapType m_GroupToLabelMap; using LabelToGroupMapType = std::map; /* Dictionary that maps between label value (key) and group id (value)*/ LabelToGroupMapType m_LabelToGroupMap; using LabelEventGuardMapType = std::map; LabelEventGuardMapType m_LabelModEventGuardMap; LookupTable::Pointer m_LookupTable; /** Indicates if the MultiLabelSegmentation allows to overwrite unlabeled pixels in normal pixel manipulation operations (e.g. TransferLabelConent).*/ bool m_UnlabeledLabelLock; /** Mutex used to secure manipulations of the internal state of label and group maps.*/ std::shared_mutex m_LabelNGroupMapsMutex; public: /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// // END FUTURE MultiLabelSegmentation /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// /** * \brief */ void UpdateCenterOfMass(PixelType pixelValue); /** * @brief Initialize an empty mitk::LabelSetImage using the information * of an mitk::Image * @param image the image which is used for initializing the mitk::LabelSetImage */ using mitk::Image::Initialize; void Initialize(const mitk::Image *image) override; /** * \brief removes all pixel content form the active layer.*/ void ClearBuffer(); /** * @brief Merges the mitk::Label with a given target value with the active label * * @param pixelValue the value of the label that should be the new merged label * @param sourcePixelValue the value of the label that should be merged into the specified one */ void MergeLabel(PixelType pixelValue, PixelType sourcePixelValue); /** * @brief Merges a list of mitk::Labels with the mitk::Label that has a specific value * * @param pixelValue the value of the label that should be the new merged label * @param vectorOfSourcePixelValues the list of label values that should be merge into the specified one */ void MergeLabels(PixelType pixelValue, const std::vector& vectorOfSourcePixelValues); /** * @brief Gets the ID of the currently active layer * @return the ID of the active layer * @pre at least on group must exist. */ unsigned int GetActiveLayer() const; - /** \brief -*/ Label* GetActiveLabel(); - /** \brief - */ + const Label* GetActiveLabel() const; /** * @brief Get the number of all existing mitk::Labels for a given layer * @param layer the layer ID for which the active mitk::Labels should be retrieved * @return the number of all existing mitk::Labels for the given layer */ unsigned int GetNumberOfLabels(unsigned int layer) const; /** * @brief Returns the number of all labels summed up across all layers * @return the overall number of labels across all layers */ unsigned int GetTotalNumberOfLabels() const; - /** - * \brief */ mitk::Image::Pointer CreateLabelMask(PixelType index); /** * @brief Initialize a new mitk::LabelSetImage by a given image. * For all distinct pixel values of the parameter image new labels will * be created. If the number of distinct pixel values exceeds mitk::Label::MAX_LABEL_VALUE * an exception will be raised. * @param image the image which is used for initialization */ void InitializeByLabeledImage(mitk::Image::Pointer image); - /** - * \brief */ void MaskStamp(mitk::Image *mask, bool forceOverwrite); - /** - * \brief */ void SetActiveLayer(unsigned int layer); void SetActiveLabel(LabelValueType label); - /** - * \brief */ unsigned int GetNumberOfLayers() const; /** * \brief Adds a new layer to the LabelSetImage. The new layer will be set as the active one. - * \param labelSet a labelset that will be added to the new layer if provided + * \param labels Labels that will be added to the new layer if provided * \return the layer ID of the new layer */ GroupIndexType AddLayer(ConstLabelVector labels = {}); /** * \brief Adds a layer based on a provided mitk::Image. * \param layerImage is added to the vector of label images * \param labels labels that will be cloned and added to the new layer if provided * \return the layer ID of the new layer */ GroupIndexType AddLayer(mitk::Image::Pointer layerImage, ConstLabelVector labels = {}); protected: mitkCloneMacro(Self); LabelSetImage(); LabelSetImage(const LabelSetImage &other); ~LabelSetImage() override; template void LayerContainerToImageProcessing(itk::Image *source, unsigned int layer); template void ImageToLayerContainerProcessing(itk::Image *source, unsigned int layer) const; template void CalculateCenterOfMassProcessing(ImageType *input, LabelValueType index); template void EraseLabelProcessing(ImageType *input, PixelType index); template void MergeLabelProcessing(ImageType *input, PixelType pixelValue, PixelType index); template void MaskStampProcessing(ImageType *input, mitk::Image *mask, bool forceOverwrite); template void InitializeByLabeledImageProcessing(LabelSetImageType *input, ImageType *other); /** helper needed for ensuring unique values. returns a sorted list of all labels (including the value for Unlabeled pixels..*/ LabelValueVectorType GetUsedLabelValues() const; std::vector m_LayerContainer; int m_ActiveLayer; bool m_activeLayerInvalid; }; /** * @brief Equal A function comparing two label set images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - LabelSetImage members * - working image data * - layer image data * - labels in label set * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolerance 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::LabelSetImage &leftHandSide, const mitk::LabelSetImage &rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two vectors of labels for being equal in data * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - Labels in vector * * @param rightHandSide An vector of labels to be compared * @param leftHandSide An vector of labels to be compared * @param eps Tolerance 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::LabelSetImage::ConstLabelVectorType& leftHandSide, const mitk::LabelSetImage::ConstLabelVectorType& rightHandSide, ScalarType eps, bool verbose); /** temporary namespace that is used until the new class MultiLabelSegmentation is introduced. It allows to already introduce/use some upcoming definitions, while refactoring code.*/ namespace MultiLabelSegmentation { enum class MergeStyle { Replace, //The old label content of a label value will be replaced by its new label content. //Therefore pixels that are labeled might become unlabeled again. //(This means that a lock of the value is also ignored). Merge //The union of old and new label content will be generated. }; enum class OverwriteStyle { RegardLocks, //Locked labels in the same spatial group will not be overwritten/changed. IgnoreLocks //Label locks in the same spatial group will be ignored, so these labels might be changed. }; } using LabelValueMappingVector = std::vector < std::pair >; /**Helper function that transfers pixels of the specified source label from source image to the destination image by using a specified destination label for a specific time step. Function processes the whole image volume of the specified time step. @remark in its current implementation the function only transfers contents of the active layer of the passed LabelSetImages. @remark the function assumes that it is only called with source and destination image of same geometry. @remark CAUTION: The function is not save if sourceImage and destinationImage are the same instance and more than one label is transferred, because the changes are made in-place for performance reasons in multiple passes. If a mapped value A equals an "old value" that occurs later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then later again, because it is also an "old" value in the mapping table. @param sourceImage Pointer to the LabelSetImage which active layer should be used as source for the transfer. @param destinationImage Pointer to the LabelSetImage which active layer should be used as destination for the transfer. @param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the label in the source image. The second element is the label that transferred pixels should become in the destination image. The order in which the labels will be transfered is the same order of elements in the labelMapping. If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transferring), keep in mind that for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be altered with MergeStyle Replace). @param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of MultiLabelSegmentation::MergeStyle. @param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see documentation of MultiLabelSegmentation::OverwriteStyle. @param timeStep indicate the time step that should be transferred. @pre sourceImage and destinationImage must be valid @pre sourceImage and destinationImage must contain the indicated timeStep @pre sourceImage must contain all indicated sourceLabels in its active layer. @pre destinationImage must contain all indicated destinationLabels in its active layer.*/ MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const LabelSetImage* sourceImage, LabelSetImage* destinationImage, const TimeStepType timeStep, LabelValueMappingVector labelMapping = { {1,1} }, MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks); /**Helper function that transfers pixels of the specified source label from source image to the destination image by using a specified destination label. Function processes the whole image volume for all time steps. For more details please see TransferLabelContentAtTimeStep for LabelSetImages. @sa TransferLabelContentAtTimeStep*/ MITKMULTILABEL_EXPORT void TransferLabelContent(const LabelSetImage* sourceImage, LabelSetImage* destinationImage, LabelValueMappingVector labelMapping = { {1,1} }, MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks); /**Helper function that transfers pixels of the specified source label from source image to the destination image by using a specified destination label for a specific time step. Function processes the whole image volume of the specified time step. @remark the function assumes that it is only called with source and destination image of same geometry. @remark CAUTION: The function is not save, if sourceImage and destinationImage are the same instance and you transfer more then one label, because the changes are made in-place for performance reasons but not in one pass. If a mapped value A equals a "old value" that is later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then latter again, because it is also an "old" value in the mapping table. @param sourceImage Pointer to the image that should be used as source for the transfer. @param destinationImage Pointer to the image that should be used as destination for the transfer. @param destinationLabelVector Reference to the vector of labels (incl. lock states) in the destination image. Unknown pixel values in the destinationImage will be assumed to be unlocked. @param sourceBackground Value indicating the background in the source image. @param destinationBackground Value indicating the background in the destination image. @param destinationBackgroundLocked Value indicating the lock state of the background in the destination image. @param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the label in the source image. The second element is the label that transferred pixels should become in the destination image. The order in which the labels will be transfered is the same order of elements in the labelMapping. If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transferring), keep in mind that for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be altered with MergeStyle Replace). @param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of MultiLabelSegmentation::MergeStyle. @param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see documentation of MultiLabelSegmentation::OverwriteStyle. @param timeStep indicate the time step that should be transferred. @pre sourceImage, destinationImage and destinationLabelVector must be valid @pre sourceImage and destinationImage must contain the indicated timeStep @pre destinationLabelVector must contain all indicated destinationLabels for mapping.*/ MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector, const TimeStepType timeStep, mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE, mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE, bool destinationBackgroundLocked = false, LabelValueMappingVector labelMapping = { {1,1} }, MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks); /**Helper function that transfers pixels of the specified source label from source image to the destination image by using a specified destination label. Function processes the whole image volume for all time steps. For more details please see TransferLabelContentAtTimeStep. @sa TransferLabelContentAtTimeStep*/ MITKMULTILABEL_EXPORT void TransferLabelContent(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector, mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE, mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE, bool destinationBackgroundLocked = false, LabelValueMappingVector labelMapping = { {1,1} }, MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks); } // namespace mitk #endif diff --git a/Modules/Multilabel/mitkMultiLabelEvents.h b/Modules/Multilabel/mitkMultiLabelEvents.h index 1ad2e95b15..8e7f491bd2 100644 --- a/Modules/Multilabel/mitkMultiLabelEvents.h +++ b/Modules/Multilabel/mitkMultiLabelEvents.h @@ -1,197 +1,196 @@ /*============================================================================ 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 mitkMultiLabelEvents_h #define mitkMultiLabelEvents_h #include #include #include namespace mitk { -#define mitkMultiLabelEventMacroDeclaration(classname, super, IDType) \ - /** \class classname */ \ +#define mitkMultiLabelEventMacroDeclaration(classname, super, IDType) \ class MITKMULTILABEL_EXPORT classname : public super \ { \ public: \ using Self = classname; \ using Superclass = super; \ classname() = default; \ classname(IDType value); \ classname(const Self & s); \ virtual ~classname() override; \ virtual const char * \ GetEventName() const override; \ virtual bool \ CheckEvent(const itk::EventObject * e) const override; \ virtual itk::EventObject * \ MakeObject() const override; \ \ private: \ void \ operator=(const Self &); \ }; \ static_assert(true, "Compile time eliminated. Used to require a semi-colon at end of macro.") #define mitkMultiLabelEventMacroDefinition(classname, super, IDType) \ classname::classname(const classname & s) \ : super(s){}; \ classname::classname(IDType value): super(value) {} \ classname::~classname() {} \ const char * classname::GetEventName() const { return #classname; } \ bool classname::CheckEvent(const itk::EventObject * e) const \ { \ if (!super::CheckEvent(e)) return false; \ return (dynamic_cast(e) != nullptr); \ } \ itk::EventObject * classname::MakeObject() const { return new classname; } \ static_assert(true, "Compile time eliminated. Used to require a semi-colon at end of macro.") /** Base event class for all events that are about a label in a MultiLabel class. * * It has a member that indicates the label id the event is refering to. * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer) * that reacts to every label and not just to a special one. */ class MITKMULTILABEL_EXPORT AnyLabelEvent : public itk::ModifiedEvent { public: using Self = AnyLabelEvent; using Superclass = itk::ModifiedEvent; const static mitk::Label::PixelType ANY_LABEL = std::numeric_limits::max(); AnyLabelEvent() = default; AnyLabelEvent(Label::PixelType labelValue); AnyLabelEvent(const Self & s); ~AnyLabelEvent() override; const char * GetEventName() const override; bool CheckEvent(const itk::EventObject * e) const override; itk::EventObject * MakeObject() const override; void SetLabelValue(Label::PixelType labelValue); Label::PixelType GetLabelValue() const; private: void operator=(const Self &); Label::PixelType m_LabelValue = std::numeric_limits::max(); }; /** Event class that is used to indicated if a label is added in a MultiLabel class. * * It has a member that indicates the label id the event is refering to. * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer) * that reacts to every label and not just to a special one. */ mitkMultiLabelEventMacroDeclaration(LabelAddedEvent, AnyLabelEvent, Label::PixelType); /** Event class that is used to indicated if a label is modified in a MultiLabel class. * * It has a member that indicates the label id the event is refering to. * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer) * that reacts to every label and not just to a special one. */ mitkMultiLabelEventMacroDeclaration(LabelModifiedEvent, AnyLabelEvent, Label::PixelType); /** Event class that is used to indicated if a label is removed in a MultiLabel class. * * It has a member that indicates the label id the event is refering to. * Use the ANY_LABEL value if you want to define an rvent (e.g. for adding an observer) * that reacts to every label and not just to a special one. */ mitkMultiLabelEventMacroDeclaration(LabelRemovedEvent, AnyLabelEvent, Label::PixelType); /** Event class that is used to indicated if a set of labels is changed in a MultiLabel class. * * In difference to the other label events LabelsChangedEvent is send only *one time* after * the modification of the MultiLableImage instance is finished. So e.g. even if 4 labels are * changed by a merge operation, this event will only be sent once (compared to LabelRemoved * or LabelModified). * It has a member that indicates the label ids the event is refering to. */ class MITKMULTILABEL_EXPORT LabelsChangedEvent : public itk::ModifiedEvent { public: using Self = LabelsChangedEvent; using Superclass = itk::ModifiedEvent; LabelsChangedEvent() = default; LabelsChangedEvent(std::vector labelValues); LabelsChangedEvent(const Self& s); ~LabelsChangedEvent() override; const char* GetEventName() const override; bool CheckEvent(const itk::EventObject* e) const override; itk::EventObject* MakeObject() const override; void SetLabelValues(std::vector labelValues); std::vector GetLabelValues() const; private: void operator=(const Self&); std::vector m_LabelValues; }; /** Base event class for all events that are about a group in a MultiLabel class. * * It has a member that indicates the group id the event is refering to. * Use the ANY_GROUP value if you want to define an event (e.g. for adding an observer) * that reacts to every group and not just to a special one. */ class MITKMULTILABEL_EXPORT AnyGroupEvent : public itk::ModifiedEvent { public: using GroupIndexType = std::size_t; using Self = AnyGroupEvent; using Superclass = itk::ModifiedEvent; const static GroupIndexType ANY_GROUP = std::numeric_limits::max(); AnyGroupEvent() = default; AnyGroupEvent(GroupIndexType groupID); AnyGroupEvent(const Self& s); ~AnyGroupEvent() override; const char* GetEventName() const override; bool CheckEvent(const itk::EventObject* e) const override; itk::EventObject* MakeObject() const override; void SetGroupID(GroupIndexType groupID); GroupIndexType GetGroupID() const; private: void operator=(const Self&); GroupIndexType m_GroupID = std::numeric_limits::max(); }; /** Event class that is used to indicated if a group is added in a MultiLabel class. * * It has a member that indicates the group id the event is refering to. * Use the ANY_GROUP value if you want to define an rvent (e.g. for adding an observer) * that reacts to every group and not just to a special one. */ mitkMultiLabelEventMacroDeclaration(GroupAddedEvent, AnyGroupEvent, AnyGroupEvent::GroupIndexType); /** Event class that is used to indicated if a group is modified in a MultiLabel class. * * It has a member that indicates the group id the event is refering to. * Use the ANY_GROUP value if you want to define an rvent (e.g. for adding an observer) * that reacts to every group and not just to a special one. */ mitkMultiLabelEventMacroDeclaration(GroupModifiedEvent, AnyGroupEvent, AnyGroupEvent::GroupIndexType); /** Event class that is used to indicated if a group is removed in a MultiLabel class. * * It has a member that indicates the group id the event is refering to. * Use the ANY_GROUP value if you want to define an rvent (e.g. for adding an observer) * that reacts to every group and not just to a special one. */ mitkMultiLabelEventMacroDeclaration(GroupRemovedEvent, AnyGroupEvent, AnyGroupEvent::GroupIndexType); } #endif diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h index a8ba18eb0b..da50f33417 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,290 +1,288 @@ /*============================================================================ 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 mitkSegTool2D_h #define mitkSegTool2D_h #include #include #include #include #include #include #include #include #include namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class MITKSEGMENTATION_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param image \param plane \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Axial --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice(const Image *image, const PlaneGeometry *plane, int &affectedDimension, int &affectedSlice); /** * @brief Updates the surface interpolations by extracting the contour form the given slice for all labels * that have a surface contour information stored for the given plane at the given timestep. - * @param slice the slice from which the contour should be extracted * @param workingImage the segmentation image * @param timeStep the time step for wich the surface interpolation information should be updated. * @param plane the plane in which the slice lies * @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the - * erosion it is most - * likely an intersecting contour an will not be added to the SurfaceInterpolationController + * erosion it is most likely an intersecting contour an will not be added to the SurfaceInterpolationController */ static void UpdateAllSurfaceInterpolations(const LabelSetImage* workingImage, TimeStepType timeStep, const PlaneGeometry *plane, bool detectIntersection); /** * \brief Extract the slice of an image that the user just scribbles on. The given component denotes the vector component of an vector image. * * \param positionEvent Event that specifies the plane that should be used to slice * \param image Image that should be sliced * \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component. * * \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem * getting the image data at that position. */ static Image::Pointer GetAffectedImageSliceAs2DImage(const InteractionPositionEvent* positionEvent, const Image* image, unsigned int component = 0); /** * \brief Extract the slice of an image cut by given plane. The given component denotes the vector component of a vector image. * * \param planeGeometry Geometry defining the slice that should be cut out. * \param image Image that should be sliced * \param timeStep TimeStep of the image that shold be sliced * \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component. * * \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem * getting the image data at that position. */ static Image::Pointer GetAffectedImageSliceAs2DImage(const PlaneGeometry* planeGeometry, const Image* image, TimeStepType timeStep, unsigned int component = 0); static Image::Pointer GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, const Image* image, TimePointType timePoint, unsigned int component = 0); /** Convenience overloaded version that can be called for a given planeGeometry, slice image and time step. * Calls static WriteBackSegmentationResults*/ static void WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep); /** Convenience overloaded version that can be called for a given planeGeometry, slice image and time step. * For more details see protected WriteSliceToVolume version.*/ static void WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo); void SetShowMarkerNodes(bool); /** * \brief Enables or disables the 3D interpolation after writing back the 2D segmentation result, and defaults to * true. */ void SetEnable3DInterpolation(bool); void Activated() override; void Deactivated() override; itkSetMacro(IsTimePointChangeAware, bool); itkGetMacro(IsTimePointChangeAware, bool); itkBooleanMacro(IsTimePointChangeAware); protected: SegTool2D(); // purposely hidden SegTool2D(const char *, const us::Module *interactorModule = nullptr); // purposely hidden ~SegTool2D() override; /** * @brief returns the segmentation node that should be modified by the tool. */ DataNode* GetWorkingDataNode() const; Image* GetWorkingData() const; DataNode* GetReferenceDataNode() const; Image* GetReferenceData() const; /** * This function can be reimplemented by derived classes to react on changes of the current * time point. Default implementation does nothing.*/ virtual void OnTimePointChanged(); struct SliceInformation { mitk::Image::ConstPointer slice; const mitk::PlaneGeometry *plane = nullptr; mitk::TimeStepType timestep = 0; unsigned int slicePosition; SliceInformation() = default; SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep); }; /** * @brief Updates the surface interpolation by extracting the contour form the given slice. * @param sliceInfos vector of slice information instances from which the contours should be extracted * @param workingImage the segmentation image * @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the * @param activeLabelValue The label value of the active label. * @param silent Indicates if the modification event of the SurfaceInterpolationController should be triggered. * erosion it is most * likely an intersecting contour an will not be added to the SurfaceInterpolationController */ static void UpdateSurfaceInterpolation(const std::vector& sliceInfos, const Image* workingImage, bool detectIntersection, mitk::Label::PixelType activeLabelValue, bool silent = false); /** * \brief Filters events that cannot be handled by 2D segmentation tools * * Currently an event is discarded if it was not sent by a 2D renderwindow and if it is * not of type InteractionPositionEvent */ bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override; /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const InteractionPositionEvent *) const; /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const InteractionPositionEvent *) const; /** Overload version that gets the reference slice passed on the passed plane geometry and timestep.*/ Image::Pointer GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const; /** Convenience version that can be called for a given event (which is used to deduce timepoint and plane) and a slice image. * Calls non static WriteBackSegmentationResults*/ void WriteBackSegmentationResult(const InteractionPositionEvent *, const Image* segmentationResult); /** Convenience version that can be called for a given planeGeometry, slice image and time step. * Calls non static WriteBackSegmentationResults*/ void WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, const Image* segmentationResult, TimeStepType timeStep); /** Overloaded version that calls the static version and also adds the contour markers. * @remark If the sliceList is empty, this function does nothing.*/ void WriteBackSegmentationResults(const std::vector &sliceList, bool writeSliceToVolume = true); /** \brief Writes all provided source slices into the data of the passed workingNode. * The function does the following: 1) for every passed slice write it to workingNode (and generate and undo/redo step); * 2) update the surface interpolation and 3) mark the node as modified. * @param workingNode Pointer to the node that contains the working image. * @param sliceList Vector of all slices that should be written into the workingNode. If the list is * empty, the function call does nothing. * @param writeSliceToVolume If set to false the write operation (WriteSliceToVolume will be skipped) * and only the surface interpolation will be updated. * @pre workingNode must point to a valid instance and contain an image instance as data.*/ static void WriteBackSegmentationResults(const DataNode* workingNode, const std::vector& sliceList, bool writeSliceToVolume = true); /** Writes a provided slice into the passed working image. The content of working image that is covered * by the slice will be completly overwritten. If asked for it also generates the needed * undo/redo steps. * @param workingImage Pointer to the image that is the target of the write operation. * @param sliceInfo SliceInfo instance that containes the slice image, the defining plane geometry and time step. * @param allowUndo Indicates if undo/redo operations should be registered for the write operation * performed by this call. true: undo/redo will be generated; false: no undo/redo will be generated, so * this operation cannot be revoked by the user. * @pre workingImage must point to a valid instance.*/ static void WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo); /** \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. By selecting this node the slicestack will be reoriented according to the passed PlanarFigure's Geometry */ int AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex); void InteractiveSegmentationBugMessage(const std::string &message) const; /** Helper function to check if a position events points to a point inside the boundingbox of a passed data instance.*/ static bool IsPositionEventInsideImageRegion(InteractionPositionEvent* positionEvent, const BaseData* data); BaseRenderer *m_LastEventSender = nullptr; unsigned int m_LastEventSlice = 0; itkGetMacro(LastTimePointTriggered, TimePointType); private: /** Internal method that gets triggered as soon as the tool manager indicates a * time point change. If the time point has changed since last time and tool * is set to be time point change aware, OnTimePointChanged() will be called.*/ void OnTimePointChangedInternal(); static void RemoveContourFromInterpolator(const SliceInformation& sliceInfo, LabelSetImage::LabelValueType labelValue); // The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; bool m_ShowMarkerNodes = false; static bool m_SurfaceInterpolationEnabled; bool m_IsTimePointChangeAware = true; TimePointType m_LastTimePointTriggered = 0.; }; } // namespace #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h index 89c772a0e6..af3e36c755 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h @@ -1,327 +1,327 @@ /*============================================================================ 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 QmitkMultiLabelInspector_h #define QmitkMultiLabelInspector_h #include #include #include #include #include #include class QmitkMultiLabelTreeModel; class QStyledItemDelegate; class QWidgetAction; namespace Ui { class QmitkMultiLabelInspector; } /* * @brief This is an inspector that offers a tree view on the labels and groups of a MultiLabelSegmentation instance. * It also allows some manipulation operations an the labels/groups accordin to the UI/selection state. */ class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelInspector : public QWidget { Q_OBJECT public: QmitkMultiLabelInspector(QWidget* parent = nullptr); ~QmitkMultiLabelInspector(); bool GetMultiSelectionMode() const; bool GetAllowVisibilityModification() const; bool GetAllowLockModification() const; bool GetAllowLabelModification() const; /** Indicates if the inspector is currently modifiying the model/segmentation. Thus as long as the manipulation is ongoing, one should assume the model to be in an invalid state.*/ bool GetModelManipulationOngoing() const; using LabelValueType = mitk::LabelSetImage::LabelValueType; using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType; /** * @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values). */ LabelValueVectorType GetSelectedLabels() const; /** @brief Returns the label that currently has the focus in the tree view. * * The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line. * * The current label must not equal the selected label(s). If the mouse is not hovering above a label * (label class or instance item), the method will return nullptr. */ mitk::Label* GetCurrentLabel() const; enum class IndexLevelType { Group, LabelClass, LabelInstance }; /** @brief Returns the level of the index that currently has the focus in the tree view. * * The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line. */ IndexLevelType GetCurrentLevelType() const; /** @brief Returns all label values that are currently affected. * * Affected means that these labels (including the one returned by GetCurrentLabel) are in the subtree of the tree * view element that currently has the focus (indicated by QTreeView::currentIndex, thus the mouse is over it and * it has a dashed border line. */ LabelValueVectorType GetCurrentlyAffactedLabelInstances() const; /** @brief Returns the values of all label instances that are of the same label (class) like the first selected label instance. * * If no label is selected an empty vector will be returned. */ LabelValueVectorType GetLabelInstancesOfSelectedFirstLabel() const; Q_SIGNALS: /** * @brief A signal that will be emitted if the selected labels change. * * @param labels A list of label values that are now selected. */ void CurrentSelectionChanged(LabelValueVectorType labels) const; /** * @brief A signal that will be emitted if the user has requested to "go to" a certain label. * * Going to a label would be e.g. to focus the renderwindows on the centroid of the label. * @param label The label that should be focused. * @param point in World coordinate that should be focused. */ void GoToLabel(LabelValueType label, const mitk::Point3D& point) const; /** @brief Signal that is emitted, if a label should be (re)named and default * label naming is deactivated. * * The instance for which a new name is requested is passed with the signal. * @param label Pointer to the instance that needs a (new) name. * @param rename Indicates if it is a renaming or naming of a new label. */ void LabelRenameRequested(mitk::Label* label, bool rename) const; /** @brief Signal that is emitted, if the model was updated (e.g. by a delete or add operation).*/ void ModelUpdated() const; - /** @briefe Signal is emitted, if the segmentation is changed that is observed by the inspector.*/ + /** @brief Signal is emitted, if the segmentation is changed that is observed by the inspector.*/ void SegmentationChanged() const; public Q_SLOTS: /** * @brief Transform a list of label values into the new selection of the inspector. * @param selectedLabels A list of selected label values. * @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers * should regard that to avoid signal loops. */ void SetSelectedLabels(const LabelValueVectorType& selectedLabels); /** * @brief The passed label will be used as new selection in the widget * @param selectedLabel Value of the selected label. * @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers * should regard that to avoid signal loops. */ void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel); /** @brief Sets the segmentation that will be used and monitored by the widget. * @param segmentation A pointer to the segmentation to set. * @remark You cannot set the segmentation directly if a segmentation node is * also set. Reset the node (nullptr) if you want to change to direct segmentation * setting. * @pre Segmentation node is nullptr. */ void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation); mitk::LabelSetImage* GetMultiLabelSegmentation() const; /** * @brief Sets the segmentation node that will be used /monitored by the widget. * * @param node A pointer to the segmentation node. * @remark If not set some features (e.g. highlighting in render windows) of the inspectors * are not active. * @remark Currently it is also needed to circumvent the fact that * modification of data does not directly trigger modification of the * node (see T27307). */ void SetMultiLabelNode(mitk::DataNode* node); mitk::DataNode* GetMultiLabelNode() const; void SetMultiSelectionMode(bool multiMode); void SetAllowVisibilityModification(bool visiblityMod); void SetAllowLockModification(bool lockMod); void SetAllowLabelModification(bool labelMod); void SetDefaultLabelNaming(bool defaultLabelNaming); /** @brief Adds an instance of the same label/class like the first label instance * indicated by GetSelectedLabels() to the segmentation. * * This new label instance is returned by the function. If the inspector has no selected label, * no new instance will be generated and nullptr will be returned. * * @remark The new label instance is a clone of the selected label instance. * Therefore all properties but the LabelValue will be the same. * * @pre AllowLabeModification must be set to true. */ mitk::Label* AddNewLabelInstance(); /** @brief Adds a new label to the segmentation. * Depending on the settings the name of * the label will be either default generated or the rename delegate will be used. The label * will be added to the same group as the first currently selected label. * * @pre AllowLabeModification must be set to true.*/ mitk::Label* AddNewLabel(); /** @brief Removes the first currently selected label instance of the segmentation. * If no label is selected * nothing will happen. * * @pre AllowLabeModification must be set to true.*/ void DeleteLabelInstance(); /** @brief Delete the first currently selected label and all its instances of the segmentation. * If no label is selected * nothing will happen. * * @pre AllowLabeModification must be set to true.*/ void DeleteLabel(); /** @brief Adds a new group with a new label to segmentation. * * @pre AllowLabeModification must be set to true.*/ mitk::Label* AddNewGroup(); /** @brief Removes the group of the first currently selected label of the segmentation. *If no label is selected nothing will happen. * * @pre AllowLabeModification must be set to true.*/ void RemoveGroup(); void SetVisibilityOfAffectedLabels(bool visible) const; void SetLockOfAffectedLabels(bool visible) const; protected: void Initialize(); void OnModelReset(); QmitkMultiLabelTreeModel* m_Model; mitk::LabelSetImage::Pointer m_Segmentation; LabelValueVectorType m_LastValidSelectedLabels; QStyledItemDelegate* m_LockItemDelegate; QStyledItemDelegate* m_ColorItemDelegate; QStyledItemDelegate* m_VisibilityItemDelegate; Ui::QmitkMultiLabelInspector* m_Controls; LabelValueVectorType GetSelectedLabelsFromSelectionModel() const; void UpdateSelectionModel(const LabelValueVectorType& selectedLabels); /** @brief Helper that returns the label object (if multiple labels are selected the first). */ mitk::Label* GetFirstSelectedLabelObject() const; mitk::Label* AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup); /**@brief Adds an instance of the same label/class like the passed label value */ mitk::Label* AddNewLabelInstanceInternal(mitk::Label* templateLabel); void RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID); void DeleteLabelInternal(const LabelValueVectorType& labelValues); private Q_SLOTS: /** @brief Transform a labels selection into a data node list and emit the 'CurrentSelectionChanged'-signal. * * The function adds the selected nodes from the original selection that could not be modified, if * m_SelectOnlyVisibleNodes is false. * This slot is internally connected to the 'selectionChanged'-signal of the selection model of the private member item view. * * @param selected The newly selected items. * @param deselected The newly deselected items. */ void OnChangeModelSelection(const QItemSelection& selected, const QItemSelection& deselected); void OnContextMenuRequested(const QPoint&); void OnAddLabel(); void OnAddLabelInstance(); void OnDeleteGroup(); void OnDeleteAffectedLabel(); void OnDeleteLabels(bool); void OnClearLabels(bool); void OnMergeLabels(bool); void OnRenameLabel(bool); void OnClearLabel(bool); void OnUnlockAffectedLabels(); void OnLockAffectedLabels(); void OnSetAffectedLabelsVisible(); void OnSetAffectedLabelsInvisible(); void OnSetOnlyActiveLabelVisible(bool); void OnItemDoubleClicked(const QModelIndex& index); void WaitCursorOn() const; void WaitCursorOff() const; void RestoreOverrideCursor() const; void PrepareGoToLabel(LabelValueType labelID) const; QWidgetAction* CreateOpacityAction(); private: bool m_ShowVisibility = true; bool m_ShowLock = true; bool m_ShowOther = false; /** @brief Indicates if the context menu allows changes in visiblity. * * Visiblity includes also color */ bool m_AllowVisibilityModification = true; /** @brief Indicates if the context menu allows changes in lock state. */ bool m_AllowLockModification = true; /** @brief Indicates if the context menu allows label modifications (adding, removing, renaming ...) */ bool m_AllowLabelModification = false; bool m_DefaultLabelNaming = true; bool m_ModelManipulationOngoing = false; mitk::DataNode::Pointer m_SegmentationNode; unsigned long m_SegmentationNodeDataMTime; mitk::ITKEventObserverGuard m_SegmentationObserver; }; #endif diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h index 8d147f1956..c683d416af 100644 --- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h +++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h @@ -1,260 +1,259 @@ /*============================================================================ 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 mitkSurfaceInterpolationController_h #define mitkSurfaceInterpolationController_h #include #include #include #include #include namespace mitk { class ComputeContourSetNormalsFilter; class CreateDistanceImageFromSurfaceFilter; class LabelSetImage; class ReduceContourSetFilter; class MITKSURFACEINTERPOLATION_EXPORT SurfaceInterpolationController : public itk::Object { public: mitkClassMacroItkParent(SurfaceInterpolationController, itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); struct MITKSURFACEINTERPOLATION_EXPORT ContourPositionInformation { Surface::ConstPointer Contour; PlaneGeometry::ConstPointer Plane; Label::PixelType LabelValue; TimeStepType TimeStep; ContourPositionInformation() : Plane(nullptr), LabelValue(std::numeric_limits::max()), TimeStep(std::numeric_limits::max()) { } ContourPositionInformation(Surface::ConstPointer contour, PlaneGeometry::ConstPointer plane, Label::PixelType labelValue, TimeStepType timeStep) : Contour(contour), Plane(plane), LabelValue(labelValue), TimeStep(timeStep) { } bool IsPlaceHolder() const { return Contour.IsNull(); } }; typedef std::vector CPIVector; static SurfaceInterpolationController *GetInstance(); /** * @brief Adds new extracted contours to the list. If one or more contours at a given position * already exist they will be updated respectively */ void AddNewContours(const std::vector& newCPIs, bool reinitializeAction = false, bool silent = false); /** - * @brief Removes the contour for a given plane for the current selected segmenation + * @brief Removes the contour for a given plane for the current selected segmentation * @param contourInfo the contour which should be removed + * @param keepPlaceholderForUndo * @return true if a contour was found and removed, false if no contour was found */ bool RemoveContour(ContourPositionInformation contourInfo, bool keepPlaceholderForUndo = false); void RemoveObservers(); /** * @brief Performs the interpolation. * */ void Interpolate(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep); /** * @brief Get the Result of the interpolation operation. * * @return mitk::Surface::Pointer */ mitk::Surface::Pointer GetInterpolationResult(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep); /** * @brief Sets the minimum spacing of the current selected segmentation * This is needed since the contour points we reduced before they are used to interpolate the surface. * * @param minSpacing Parameter to set */ void SetMinSpacing(double minSpacing); /** * @brief Sets the minimum spacing of the current selected segmentation * This is needed since the contour points we reduced before they are used to interpolate the surface * @param maxSpacing Set the max Spacing for interpolation */ void SetMaxSpacing(double maxSpacing); /** * Sets the volume i.e. the number of pixels that the distance image should have * By evaluation we found out that 50.000 pixel delivers a good result */ void SetDistanceImageVolume(unsigned int distImageVolume); /** * @brief Get the current selected segmentation for which the interpolation is performed * @return the current segmentation image */ mitk::LabelSetImage* GetCurrentSegmentation(); void SetDataStorage(DataStorage::Pointer ds); /** * Sets the current list of contourpoints which is used for the surface interpolation * @param currentSegmentationImage The current selected segmentation */ void SetCurrentInterpolationSession(LabelSetImage* currentSegmentationImage); /** * @brief Remove interpolation session * @param segmentationImage the session to be removed */ void RemoveInterpolationSession(const LabelSetImage* segmentationImage); /** * @brief Removes all sessions */ void RemoveAllInterpolationSessions(); /** * @brief Get the Contours at a certain timeStep and layerID. * * @param timeStep Time Step from which to get the contours. * @param labelValue label from which to get the contours. * @return std::vector Returns contours. */ CPIVector* GetContours(LabelSetImage::LabelValueType labelValue, TimeStepType timeStep); std::vector GetAffectedLabels(const LabelSetImage* seg, TimeStepType timeStep, const PlaneGeometry* plane) const; /** * @brief Triggered with the "Reinit Interpolation" action. The contours are used to repopulate the * surfaceInterpolator data structures so that interpolation can be performed after reloading data. - * - * @param contourList List of contours extracted - * @param contourPlanes List of planes at which the contours were extracted */ void CompleteReinitialization(const std::vector& newCPIs); /** * @brief Removes contours of a particular label and at a given time step for the current session/segmentation. * + * @param segmentationImage * @param label Label of contour to remove. * @param timeStep Time step in which to remove the contours. * @remark if the label or time step does not exist, nothing happens. */ void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label, TimeStepType timeStep); /** * @brief Removes contours of a particular label and at a given time step for the current session/segmentation. * + * @param segmentationImage * @param label Label of contour to remove. - * @param timeStep Time step in which to remove the contours. * @remark if the label or time step does not exist, nothing happens. */ void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label); unsigned int GetNumberOfInterpolationSessions(); /** * @brief Get the Segmentation Image Node object * * @return DataNode* returns the DataNode containing the segmentation image. */ mitk::DataNode* GetSegmentationImageNode() const; protected: SurfaceInterpolationController(); ~SurfaceInterpolationController() override; template void GetImageBase(itk::Image *input, itk::ImageBase<3>::Pointer &result); private: /** * @brief * * @param caller * @param event */ void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event); /** * @brief Function that removes contours of a particular label when the "Remove Label" event is triggered in the labelSetImage. * */ void OnRemoveLabel(const itk::Object* caller, const itk::EventObject& event); /** * @brief When a new contour is added to the pipeline or an existing contour is replaced, * the plane geometry information of that contour is added as a child node to the * current node of the segmentation image. This is useful in the retrieval of contour information * when data is reloaded after saving. * * @param contourInfo contourInfo struct to add to data storage. */ void AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo) const; DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode) const; DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue) const; DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) const; /** * Adds Contours from the active Label to the interpolation pipeline */ void AddActiveLabelContoursForInterpolation(ReduceContourSetFilter* reduceFilter, const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep); /** * @brief Clears the interpolation data structures. Called from CompleteReinitialization(). * */ void ClearInterpolationSession(); void RemoveObserversInternal(const mitk::LabelSetImage* segmentationImage); /** * @brief Add contour to the interpolation pipeline * * @param contourInfo Contour information to be added * @param reinitializationAction If the contour is coming from a reinitialization process or not */ void AddToCPIMap(ContourPositionInformation& contourInfo, bool reinitializationAction = false); unsigned int m_DistanceImageVolume; mitk::DataStorage::Pointer m_DataStorage; WeakPointer m_SelectedSegmentation; }; } #endif