diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h index 4758746f44..87cd98e69f 100644 --- a/Modules/Core/include/mitkPropertyRelationRuleBase.h +++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h @@ -1,374 +1,392 @@ /*============================================================================ 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 mitkPropertyRelationRuleBase_h #define mitkPropertyRelationRuleBase_h #include "mitkIPropertyOwner.h" #include "mitkIdentifiable.h" #include "mitkException.h" #include "mitkNodePredicateBase.h" #include "mitkPropertyKeyPath.h" #include #include namespace mitk { /**Base class to standardize/abstract/encapsulate rules and business logic to detect and define (property/data based) relations in MITK. Following important definitions must be regarded when using/implementing/specifing rule classes: - Releations represented by rules are directed relations that point from a source IPropertyOwner (Source) to a destination IPropertyOwner (Destination). - Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations. Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules, abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule: in its abstract state it cannot connect but be used to detect any type of generic ID relation. - A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the way to express the source image relation to an input image and to a mask would be nearly the same and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for "DICOM source mask" and both rules must have distinct rule IDs. - Source may have several relations of a rule to different Destinations. Destination may have several relations of a rule from different Sources. But a specific source destination pair may have only one relation of a specific rule id (concrete rule). A specific source destination pair may however have multiple relations for an abstract rule. - The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation but it should still be documented, even if the destination is unknown. One has to explicitly disconnect a zombie relation to get rid of it. - Each relation has its own UID (relationUID) that can be used to address it. The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer which uses the properties of Source and Destination to deduce if there is a relation of the rule type. The ID-layer is completely implemented by this base class. The base class falls back to the Data-layer (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated to (only) look at the data layer. Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction. Rules use relation instance identifing (RII) properties in order to manage their relations that are stored in the Source. The RII-properties follow the following naming schema: "MITK.Relations..[relationUID|destinationUID|ruleID|]" - : The unique index of the relation for the Source. Used to assign/group the properties to their relation. In the default implementation of this class the instance id is an positive integer (i>0). - relationUID: The UID of the relation. Set by the ID-layer (so by this class) - destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements IIdentifiable. - ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set automaticaly be this base class. - : Information needed by the Data-layer (so derived classes) to find the relationUID */ class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object { public: mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); using RuleIDType = std::string; using RelationUIDType = Identifiable::UIDType; using RelationUIDVectorType = std::vector; /** Enum class for different types of relations. */ enum class RelationType { None = 0, /**< Two IPropertyOwner have no relation under the rule.*/ Data = 1, /**< Two IPropertyOwner have a relation, but it is "only" deduced from the Data-layer (so a bit "weaker" as ID). Reasons for the missing ID connection could be that Destintination has not IIdentifiable implemented.*/ ID = 2, /**< Two IPropertyOwner have a relation and are explictly connected via the ID of IIdentifiable of the Destination.*/ Complete = 3 /**< Two IPropertyOwner have a relation and are fully explictly connected (via data layer and ID layer).*/ }; using RelationVectorType = std::vector; /** Returns the generic root path for relation rules ("MITK.Relations").*/ static PropertyKeyPath GetRootKeyPath(); + using InstanceIDType = std::string; + /** Returns the property key path for a RII property. + * @param propName If not empty a PropertyPath element will added (with the passed value) after the element. + * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, + * it is wildcarded and will match RIIs property of any instance.*/ + static PropertyKeyPath GetRIIPropertyKeyPath(const std::string propName, const InstanceIDType& instanceID); + /** Returns the property key path for RII RelationUID properties. + * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, + * it is wildcarded and will match RII RelationUIDs property of any instance.*/ + static PropertyKeyPath GetRIIRelationUIDPropertyKeyPath(const InstanceIDType& instanceID = ""); + /** Returns the property key path for RII RuleID properties. + * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, + * it is wildcarded and will match RII RuleIDs property of any instance.*/ + static PropertyKeyPath GetRIIRuleIDPropertyKeyPath(const InstanceIDType& instanceID = ""); + /** Returns the property key path for RII DestinationUID properties. + * @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty, + * it is wildcarded and will match RII DestinationUIDs property of any instance.*/ + static PropertyKeyPath GetRIIDestinationUIDPropertyKeyPath(const InstanceIDType& instanceID = ""); + /** Returns an ID string that identifies the rule class. @post The returned rule ID must met the preconditions of a PropertyKeyPath element name (see mitk::PropertyKeyPath*/ virtual RuleIDType GetRuleID() const = 0; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ virtual std::string GetDisplayName() const = 0; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ virtual std::string GetSourceRoleName() const = 0; /** Returns a human readable string that can be used to describe the role of a destionation in context of the rule * instance.*/ virtual std::string GetDestinationRoleName() const = 0; /** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement if another behavior is needed.*/ virtual bool IsAbstract() const; /** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsSourceCandidate(const IPropertyProvider *owner) const; /** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const; /** Returns true if the passed owner is a Source of a relation defined by the rule. @pre owner must be a pointer to a valid IPropertyProvider instance.*/ bool IsSource(const IPropertyProvider *owner) const; /** Returns all relation types of the passed IPropertyOwner instances. @remark Abstract rules may have several relationtypes between the instances (from different supported concrete rules), that cover both ID and Data relations; thus it returns a vector of RelationTypes. @result Vector of all relation types that exist between the given instances. Empty vector equals none relation at all. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ RelationVectorType GetRelationTypes(const IPropertyProvider* source, const IPropertyProvider* destination) const; /** Indicates if passed IPropertyOwner instances have a relation of a certain type. @remark Abstract rules may also indicate RelationType::Complete if there are multiple relations (from different supported concrete rules), that cover both ID and Data relations. @param requiredRelation Defines the type of relation that should be present. None: does not matter which one, as long as at least one is present. Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ bool HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination, RelationType requiredRelation = RelationType::None) const; /** Returns a vector of relation UIDs for all relations of this rule instance that are defined for the passed source. @pre source must be a pointer to a valid IPropertyOwner instance. @param layer Defines the layer the relations must be reflected. None: does not matter which one, as long as at least one is present. Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.*/ RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source, RelationType layer = RelationType::None) const; /** Returns the relation UID(s) for the passed source and destination of this rule instance. If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only one relation UID. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance.*/ RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Returns the relation UID for the passed source and destination of this rule instance. If the passed instances have no relation, no ID can be deduced and an exception will be thrown. If more than one relation is found, also an exception will be thrown. Thus only use this convenience method, if you are sure that one(!) relation UID can exist. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance. @pre Source and destination have one relation; otherwise if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists a default MITK expception is thrown.*/ RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const; /**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet). Thus all nodes where IsSourceCandidate() returns true. */ NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const; /**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination yet). Thus all nodes where IsDestinationCandidate() returns true. */ NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const; /**Predicate that can be used to find nodes that are Sources of that rule and connected. Thus all nodes where IsSource() returns true.*/ NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const; /**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule @param destination Pointer to the Destination instance that should be used for detection. @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetSourcesDetector( const IPropertyProvider *destination, RelationType exclusiveRelation = RelationType::None) const; /**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule @param source Pointer to the Source instance that should be used for detection. @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. - @pre Destination must be a valid instance.*/ + @pre Source must be a valid instance.*/ NodePredicateBase::ConstPointer GetDestinationsDetector( const IPropertyProvider *source, RelationType exclusiveRelation = RelationType::None) const; /**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID. @param source Pointer to the Source instance that should be used for detection. @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source, RelationUIDType relationUID) const; /**Disconnect the passed instances by modifing source. One can specify which layer should be disconnected via the argument "layer". Default is the complete disconnection. All RII-properties or properties that define the connection on the data layer in the source for the passed destination will be removed. @pre source must be a valid instance. @pre destination must be a valid instance. @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer = RelationType::Complete) const; /**Disconnect the source from the passed relationUID (usefull for "zombie relations"). One can specify which layer should be disconnected via the argument "layer". Default is the complete disconnection. All RII-properties or properties that define the connection on the data layer in the source for the passed destination will be removed. If the relationUID is not part of the source. Nothing will be changed. @pre source must be a valid instance. @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ void Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer = RelationType::Complete) const; /**Returns the list of PropertyKeyPaths of all properties that are relevant for a given relation. @param source Pointer to the Source instance that containes the potential properties. @param relationUID UID of the relation that is relevant for the requested properties. @param layer Indicates which layer is requested. ID: returns all RII properties that belong to the relation. Data: returns all properties that are relevant/belong to the data layer of the relation. Complete: returns all properties (ID+Data) @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ std::vector GetReleationPropertyPaths(const IPropertyProvider* source, RelationUIDType relationUID, RelationType layer = RelationType::Data) const; protected: PropertyRelationRuleBase() = default; ~PropertyRelationRuleBase() override = default; - using InstanceIDType = std::string; using InstanceIDVectorType = std::vector; static InstanceIDType NULL_INSTANCE_ID(); /** Returns the instance IDs for the passed source and destination for this rule instance. If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID), an empty vector will be returned. @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider pair and concrete rule. But there might be more then one instanceID because either 1) the rule is abstract and supports multiple rule IDs or 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance.*/ InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source, const IPropertyProvider *destination) const; using DataRelationUIDVectorType = std::vector< std::pair >; /** Returns the ReleationUIDs of all relations that are defined by the data layer of source for this rule instance and, if defined, destination. If the passed source (and destination) instance has no relation on the data layer, an empty vector will be returned. @remark Per definition for property relation rules only 0 or 1 instance should be found for one provider pair and concrete rule. But there might be more then one instance because either 1) the rule is abstract and supports multiple rule IDs or 2) the data layer may be ambiguous (e.g. because the destination was not specified) and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @param destination Destination the find relations should point to. If destination is NULL any relation on the data layer for this rule and source are wanted. @param instances_IDLayer List of releation instances that are already defined by the ID layer. The implementation of this function should only cover releations that are not already resembled in the passed relarions_IDLayer.*/ virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider * source, const IPropertyProvider * destination, const InstanceIDVectorType& instances_IDLayer) const = 0; /**Helper function that deduces the relation UID of the given relation instance. If it cannot be deduced an NoPropertyRelationException is thrown.*/ RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; /**Helper function that deduces the relation instance ID given the relation UID. If it cannot be deduced an NoPropertyRelationException is thrown.*/ InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source, const RelationUIDType &relationUID) const; /**Explicitly connects the passed instances. Afterwards they have a relation of Data (if data layer is supported), ID (if a destination implements IIdentifiable) or Complete (if Data and ID could be connected). If the passed instance are already connected the old connection will be overwritten (and raised to the highest possible connection level). @remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations. @pre source must be a valid instance. @pre destination must be a valid instance. @pre the rule instance must not be abstract. @return Return the relation uid of the relation connected by this method call*/ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; /**Is called by Connect() to ensure that source has correctly set properties to resemble the relation on the data layer. This means that the method should set the properties that describe and encode the relation on the data layer (data-layer-specific relation properties). If the passed instance are already connected, the old settings should be overwritten. Connect() will ensure that source and destination are valid pointers. @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance is ensured. @pre source must be a valid instance. @pre destination must be a valid instance.*/ virtual void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const = 0; /**This method is called by Disconnect() to remove all properties of the relation from the source that are set by Connect_datalayer(). @remark This method should remove all properties that are set for a specific relation by Connect_datalayer(...). If the relationUID is not part of the source, nothing will be changed. Disconnect() ensures that source is a valid pointer if called. @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ virtual void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const = 0; /** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because it is an abstract rule which also supports the rule ID. @return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/ virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const; /** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance. - * @param propName If not empty a PropertyPath element will added (with the passed value) after the element. + * @param propName If not empty a PropertyPath element will be added (with the passed value) after the element. * @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/ std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const; /**Helper function that deduces the instance ID out of a property name. If it cannot be deduced an MITK exception is thrown.*/ static InstanceIDType GetInstanceIDByPropertyName(const std::string propName); /**Helper function that retrives the rule ID of a relation instance of a passed source. @pre source must be valid. @pre source must have a relation instance with this ID*/ RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; /**Helper function that retrives the destination UID of a relation instance of a passed source. If the relation has no destination UID, an empty string will be returned. @pre source must be valid.*/ std::string GetDestinationUIDByInstanceID(const IPropertyProvider * source, const InstanceIDType & instanceID) const; itk::LightObject::Pointer InternalClone() const override; /** helper method that serves as a workaround until T24729 is done. Please remove if T24728 is done then could directly use owner->GetPropertyKeys() again.*/ static std::vector GetPropertyKeys(const IPropertyProvider *owner); /** Helper method that tries to cast the provider to the Identifiable interface.*/ const Identifiable* CastProviderAsIdentifiable(const mitk::IPropertyProvider* provider) const; private: /** Creats a relation UID*/ static RelationUIDType CreateRelationUID(); /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const; }; /**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/ class NoPropertyRelationException : public Exception { public: mitkExceptionClassMacro(NoPropertyRelationException, Exception) }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkSourceImageRelationRule.h b/Modules/Core/include/mitkSourceImageRelationRule.h index 037959ca07..b6647a871f 100644 --- a/Modules/Core/include/mitkSourceImageRelationRule.h +++ b/Modules/Core/include/mitkSourceImageRelationRule.h @@ -1,129 +1,129 @@ /*============================================================================ 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 mitkGenericIDRelationRule_h -#define mitkGenericIDRelationRule_h +#ifndef MITK_SOURCE_IMAGE_RELATION_RULE_H +#define MITK_SOURCE_IMAGE_RELATION_RULE_H #include "mitkPropertyRelationRuleBase.h" #include "mitkImage.h" namespace mitk { /**This rule class can be used for relations that reference an image as source for a destination entity. (e.g. an image that is used to generate the relation source). The ID-layer is supported like for GenericIDReleations. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In addition the rule uses the data-layer to deduce/define relations. For this layer it uses properties compliant to DICOM. Thus (1) the information is stored in a DICOM Source Image Sequence item (0x0008,0x2112) and (2) the destination must have properties DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016). If the destination does not have this properties, no connection can be made on the data-layer. @remark Please note that PropertyRelationRules and DICOM use the term "source" differently. The DICOM source (image) equals the PropertyRelationRule destination. This is due to an inverted relation direction. So in the context of the SourceImageRelationRule interface a derived data is the source and points to the original image, it derives from. In the context of DICOM this referenced original image would be called source image (as the name of this class). In order to be able to use this class for different relation types (DICOM would call it purposes), the purposeTag is used. It must be specified when creating a rule instance. The purposeTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. One may also specify the display name and the role names of the instance. If not specified the default values are used (display name: " relation", source role name: "derived data", destination role name: "source image") */ class MITKCORE_EXPORT SourceImageRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(SourceImageRelationRule, PropertyRelationRuleBase); itkNewMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; bool IsDestinationCandidate(const IPropertyProvider *owner) const override; /** Connects to passed images. @remark destination must specifiy DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016) in order to establish a connection on the data layer.*/ RelationUIDType Connect(Image *source, const Image *destination) const; protected: SourceImageRelationRule(); SourceImageRelationRule(const RuleIDType &purposeTag); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~SourceImageRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 (and its associated ruleID) that refer to destination or all (if no destination is passed) with a supported RuleID and which are not already covered by the ignoreInstances.*/ std::vector > GetReferenceSequenceIndices(const IPropertyProvider * source, const IPropertyProvider* destination = nullptr, InstanceIDVectorType ignoreInstances = {}) const; using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; /**Prepares a new reference to an image on the data layer. Therefore an unused and valid sequence item index for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const; std::string GenerateRuleID(const std::string& purpose) const; private: RuleIDType m_PurposeTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp index 5e4e81c3d6..58f507e211 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -1,852 +1,871 @@ /*============================================================================ 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 "mitkPropertyRelationRuleBase.h" #include #include #include #include #include #include #include #include bool mitk::PropertyRelationRuleBase::IsAbstract() const { return true; } bool mitk::PropertyRelationRuleBase::IsSourceCandidate(const IPropertyProvider *owner) const { return owner != nullptr; } bool mitk::PropertyRelationRuleBase::IsDestinationCandidate(const IPropertyProvider *owner) const { return owner != nullptr; } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRootKeyPath() { return PropertyKeyPath().AddElement("MITK").AddElement("Relations"); } bool mitk::PropertyRelationRuleBase::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID(); } - -std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const +mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath(const std::string propName, const InstanceIDType& instanceID) { - auto path = this->GetRootKeyPath(); + auto path = GetRootKeyPath(); if (instanceID.empty()) { path.AddAnyElement(); } else { path.AddElement(instanceID); } if (!propName.empty()) { path.AddElement(propName); } - return PropertyKeyPathToPropertyRegEx(path); + return path; +} + +std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const +{ + return PropertyKeyPathToPropertyRegEx(GetRIIPropertyKeyPath(propName, instanceID)); +} + +mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath(const InstanceIDType& instanceID) +{ + return GetRIIPropertyKeyPath("relationUID", instanceID); +} + +mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath(const InstanceIDType& instanceID) +{ + return GetRIIPropertyKeyPath("ruleID", instanceID); +} + +mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath(const InstanceIDType& instanceID) +{ + return GetRIIPropertyKeyPath("destinationUID", instanceID); } //workaround until T24729 is done. Please remove if T24728 is done //then could directly use owner->GetPropertyKeys() again. std::vector mitk::PropertyRelationRuleBase::GetPropertyKeys(const mitk::IPropertyProvider *owner) { std::vector keys; auto sourceCasted = dynamic_cast(owner); if (sourceCasted) { auto sourceData = sourceCasted->GetData(); if (sourceData) { keys = sourceData->GetPropertyKeys(); } else { keys = sourceCasted->GetPropertyKeys(); } } else { keys = owner->GetPropertyKeys(); } return keys; } //end workaround for T24729 bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const { return !this->GetExistingRelations(owner).empty(); } bool mitk::PropertyRelationRuleBase::HasRelation( const IPropertyProvider* source, const IPropertyProvider* destination, RelationType requiredRelation) const { auto relTypes = this->GetRelationTypes(source, destination); if (requiredRelation == RelationType::None) { return !relTypes.empty(); } RelationVectorType allowedTypes = { RelationType::Complete }; if (requiredRelation == RelationType::Data) { allowedTypes.emplace_back(RelationType::Data); } else if (requiredRelation == RelationType::ID) { allowedTypes.emplace_back(RelationType::ID); } return relTypes.end() != std::find_first_of(relTypes.begin(), relTypes.end(), allowedTypes.begin(), allowedTypes.end()); } mitk::PropertyRelationRuleBase::RelationVectorType mitk::PropertyRelationRuleBase::GetRelationTypes( const IPropertyProvider* source, const IPropertyProvider* destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed owner pointer is NULL"; } auto instanceIDs_IDLayer = this->GetInstanceID_IDLayer(source, destination); auto relIDs_dataLayer = this->GetRelationUIDs_DataLayer(source, destination, {}); if (relIDs_dataLayer.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Relation UID: " << relIDs_dataLayer.front().first; } bool hasComplete = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); bool hasID = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relIDs_dataLayer.end() == std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); bool hasData = relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return instanceIDs_IDLayer.end() == std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); RelationVectorType result; if (hasData) { result.emplace_back(RelationType::Data); } if (hasID) { result.emplace_back(RelationType::ID); } if (hasComplete) { result.emplace_back(RelationType::Complete); } return result; } mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( const IPropertyProvider *source, RelationType layer) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } RelationUIDVectorType relationUIDs; InstanceIDVectorType instanceIDs; if (layer != RelationType::Data) { auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); auto regEx = std::regex(ruleIDRegExStr); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto& key : keys) { if (std::regex_match(key, regEx)) { auto idProp = source->GetConstProperty(key); auto ruleID = idProp->GetValueAsString(); if (this->IsSupportedRuleID(ruleID)) { auto instanceID = this->GetInstanceIDByPropertyName(key); instanceIDs.emplace_back(instanceID); relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } } } } if (layer == RelationType::ID) { return relationUIDs; } DataRelationUIDVectorType relationUIDandRuleID_Data; if (layer != RelationType::ID) { relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source, nullptr, instanceIDs); } RelationUIDVectorType relationUIDs_Data; std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relationUIDs_Data), [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); if (layer == RelationType::Data) { return relationUIDs_Data; } std::sort(relationUIDs.begin(), relationUIDs.end()); std::sort(relationUIDs_Data.begin(), relationUIDs_Data.end()); RelationUIDVectorType result; if (layer == RelationType::Complete) { std::set_intersection(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); } else { std::set_union(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); } return result; } mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetRelationUIDs( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } RelationUIDVectorType relUIDs_id; auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); for (const auto& instanceID : instanceIDs) { relUIDs_id.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } DataRelationUIDVectorType relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source,destination,instanceIDs); RelationUIDVectorType relUIDs_Data; std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relUIDs_Data), [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); std::sort(relUIDs_id.begin(), relUIDs_id.end()); std::sort(relUIDs_Data.begin(), relUIDs_Data.end()); RelationUIDVectorType result; std::set_union(relUIDs_id.begin(), relUIDs_id.end(), relUIDs_Data.begin(), relUIDs_Data.end(), std::back_inserter(result)); return result; } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const { auto result = this->GetRelationUIDs(source, destination); if (result.empty()) { mitkThrowException(NoPropertyRelationException); } else if(result.size()>1) { mitkThrow() << "Cannot return one(!) relation UID. Multiple relations exists for given rule, source and destination."; } return result[0]; } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::NULL_INSTANCE_ID() { return std::string(); }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUIDByInstanceID( const IPropertyProvider *source, const InstanceIDType &instanceID) const { RelationUIDType result; if (instanceID != NULL_INSTANCE_ID()) { auto idProp = source->GetConstProperty( - PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(instanceID).AddElement("relationUID"))); + PropertyKeyPathToPropertyName(GetRIIRelationUIDPropertyKeyPath(instanceID))); if (idProp.IsNotNull()) { result = idProp->GetValueAsString(); } } if (result.empty()) { mitkThrowException(NoPropertyRelationException); } return result; } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByRelationUID( const IPropertyProvider *source, const RelationUIDType &relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } InstanceIDType result = NULL_INSTANCE_ID(); auto destRegExStr = - PropertyKeyPathToPropertyRegEx(GetRootKeyPath().AddAnyElement().AddElement("relationUID")); + PropertyKeyPathToPropertyRegEx(GetRIIRelationUIDPropertyKeyPath()); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == relationUID) { if (instance_matches.size()>1) { result = instance_matches[1]; break; } } } } return result; } mitk::PropertyRelationRuleBase::InstanceIDVectorType mitk::PropertyRelationRuleBase::GetInstanceID_IDLayer( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto identifiable = CastProviderAsIdentifiable(destination); InstanceIDVectorType result; if (identifiable) { // check for relations of type Connected_ID; auto destRegExStr = this->GetRIIPropertyRegEx("destinationUID"); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto destUID = identifiable->GetUID(); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == destUID) { if (instance_matches.size()>1) { auto instanceID = instance_matches[1]; if (this->IsSupportedRuleID(GetRuleIDByInstanceID(source, instanceID))) { result.push_back(instanceID); } } } } } } return result; } const mitk::Identifiable* mitk::PropertyRelationRuleBase::CastProviderAsIdentifiable(const mitk::IPropertyProvider* destination) const { auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. //This could be removed at the point we can get rid of DataNodes or they get realy transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } return identifiable; } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } if (this->IsAbstract()) { mitkThrow() << "Error. This is an abstract property relation rule. Abstract rule must not make a connection. Please use a concrete rule."; } auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); bool hasIDlayer = !instanceIDs.empty(); auto relUIDs_data = this->GetRelationUIDs_DataLayer(source, destination, {}); if (relUIDs_data.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. RelationUID ID: " << relUIDs_data.front().first; } bool hasDatalayer = !relUIDs_data.empty(); RelationUIDType relationUID = this->CreateRelationUID(); InstanceIDType instanceID = NULL_INSTANCE_ID(); if (hasIDlayer) { instanceID = instanceIDs.front(); } else if (hasDatalayer) { try { instanceID = this->GetInstanceIDByRelationUID(source, relUIDs_data.front().first); } catch(...) { } } if(instanceID == NULL_INSTANCE_ID()) { instanceID = this->CreateNewRelationInstance(source, relationUID); } auto relUIDKey = - PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("relationUID")); + PropertyKeyPathToPropertyName(GetRIIRelationUIDPropertyKeyPath(instanceID)); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); auto ruleIDKey = - PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("ruleID")); + PropertyKeyPathToPropertyName(GetRIIRuleIDPropertyKeyPath(instanceID)); source->SetProperty(ruleIDKey, mitk::StringProperty::New(this->GetRuleID())); if (!hasIDlayer) { auto identifiable = this->CastProviderAsIdentifiable(destination); if (identifiable) { auto destUIDKey = - PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID")); + PropertyKeyPathToPropertyName(GetRIIDestinationUIDPropertyKeyPath(instanceID)); source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); } } this->Connect_datalayer(source, destination, instanceID); return relationUID; } void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer) const { if (source == nullptr) { mitkThrow() << "Error. Source is invalid. Cannot disconnect."; } if (destination == nullptr) { mitkThrow() << "Error. Destination is invalid. Cannot disconnect."; } try { const auto relationUIDs = this->GetRelationUIDs(source, destination); for (const auto& relUID: relationUIDs) { this->Disconnect(source, relUID, layer); } } catch (const NoPropertyRelationException &) { // nothing to do and no real error in context of disconnect. } } void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer) const { if (source == nullptr) { mitkThrow() << "Error. Source is invalid. Cannot disconnect."; } if (layer == RelationType::Data || layer == RelationType::Complete) { this->Disconnect_datalayer(source, relationUID); } auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); if ((layer == RelationType::ID || layer == RelationType::Complete) && instanceID != NULL_INSTANCE_ID()) { auto instancePrefix = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(instancePrefix) == 0) { source->RemoveProperty(key); } } } } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::CreateRelationUID() { UIDGenerator generator; return generator.GetUID(); } /**This mutex is used to guard mitk::PropertyRelationRuleBase::CreateNewRelationInstance by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ std::mutex relationCreationLock; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::CreateNewRelationInstance( IPropertyOwner *source, const RelationUIDType &relationUID) const { std::lock_guard guard(relationCreationLock); ////////////////////////////////////// // Get all existing instanc IDs std::vector instanceIDs; InstanceIDType newID = "1"; auto destRegExStr = - PropertyKeyPathToPropertyRegEx(this->GetRootKeyPath().AddAnyElement().AddElement("relationUID")); + PropertyKeyPathToPropertyRegEx(GetRIIRelationUIDPropertyKeyPath()); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = std::to_string(instanceIDs.back() + 1); } ////////////////////////////////////// // reserve new ID auto relUIDKey = - PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(newID).AddElement("relationUID")); + PropertyKeyPathToPropertyName(GetRIIRelationUIDPropertyKeyPath(newID)); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); return newID; } itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const { return Superclass::InternalClone(); } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByPropertyName(const std::string propName) { auto proppath = PropertyNameToPropertyKeyPath(propName); auto ref = GetRootKeyPath(); if (proppath.GetSize() < 3 || !(proppath.GetFirstNode() == ref.GetFirstNode()) || !(proppath.GetNode(1) == ref.GetNode(1))) { mitkThrow() << "Property name is not for a RII property or containes no instance ID. Wrong name: " << propName; } return proppath.GetNode(2).name; } mitk::PropertyRelationRuleBase::RuleIDType mitk::PropertyRelationRuleBase::GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } - auto path = GetRootKeyPath().AddElement(instanceID).AddElement("ruleID"); + auto path = GetRIIRuleIDPropertyKeyPath(instanceID); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } if (result.empty()) { mitkThrowException(NoPropertyRelationException) << "Error. Source has no property relation with the passed instance ID. Instance ID: " << instanceID; } return result; } std::string mitk::PropertyRelationRuleBase::GetDestinationUIDByInstanceID(const IPropertyProvider* source, const InstanceIDType& instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } - auto path = GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID"); + auto path = GetRIIDestinationUIDPropertyKeyPath(instanceID); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } return result; } namespace mitk { /** * \brief Predicate used to wrap rule checks. * * \ingroup DataStorage */ class NodePredicateRuleFunction : public NodePredicateBase { public: using FunctionType = std::function; mitkClassMacro(NodePredicateRuleFunction, NodePredicateBase) mitkNewMacro2Param(NodePredicateRuleFunction, const FunctionType &, PropertyRelationRuleBase::ConstPointer) ~NodePredicateRuleFunction() override = default; bool CheckNode(const mitk::DataNode *node) const override { if (!node) { return false; } return m_Function(node, m_Rule); }; protected: explicit NodePredicateRuleFunction(const FunctionType &function, PropertyRelationRuleBase::ConstPointer rule) : m_Function(function), m_Rule(rule) { }; FunctionType m_Function; PropertyRelationRuleBase::ConstPointer m_Rule; }; } // namespace mitk mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourceCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSourceCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsDestinationCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetConnectedSourcesDetector() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSource(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourcesDetector( const IPropertyProvider *destination, RelationType exclusiveRelation) const { if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto check = [destination, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(node, destination, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( const IPropertyProvider *source, RelationType exclusiveRelation) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto check = [source, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(source, node, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationDetector( const IPropertyProvider *source, RelationUIDType relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto relUIDs = this->GetExistingRelations(source); if (std::find(relUIDs.begin(), relUIDs.end(), relationUID) == relUIDs.end()) { mitkThrow() << "Error. Passed relationUID does not identify a relation instance of the passed source for this rule instance."; }; auto check = [source, relationUID](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { try { auto relevantUIDs = rule->GetRelationUIDs(source, node); for (const auto& aUID : relevantUIDs) { if (aUID == relationUID) { return true; } } } catch(const NoPropertyRelationException &) { return false; } return false; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } diff --git a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp index 6d9b58e301..de0916c8d5 100644 --- a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp +++ b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp @@ -1,414 +1,419 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include "mitkSourceImageRelationRule.h" #include "mitkPropertyNameHelper.h" #include "mitkStringProperty.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkDataNode.h" #include "mitkIdentifiable.h" std::string mitk::SourceImageRelationRule::GenerateRuleID(const std::string& purpose) const { - return "SourceImageRelation " + purpose; + std::string result = "SourceImageRelation"; + if (!purpose.empty()) + { + result += " " + purpose; + } + return result; } bool mitk::SourceImageRelationRule::IsAbstract() const { return m_PurposeTag.empty(); }; bool mitk::SourceImageRelationRule::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("SourceImageRelation ") == 0); }; mitk::SourceImageRelationRule::RuleIDType mitk::SourceImageRelationRule::GetRuleID() const { return this->GenerateRuleID(m_PurposeTag); }; std::string mitk::SourceImageRelationRule::GetDisplayName() const { return m_DisplayName; }; std::string mitk::SourceImageRelationRule::GetSourceRoleName() const { return m_SourceRole; }; std::string mitk::SourceImageRelationRule::GetDestinationRoleName() const { return m_DestinationRole; }; bool mitk::SourceImageRelationRule::IsDestinationCandidate(const IPropertyProvider *owner) const { auto node = dynamic_cast(owner); auto image = nullptr != node ? dynamic_cast(node->GetData()) : dynamic_cast(owner); return image != nullptr; } mitk::SourceImageRelationRule::RelationUIDType mitk::SourceImageRelationRule::Connect(Image *source, const Image *destination) const { return Superclass::Connect(source, destination); }; mitk::SourceImageRelationRule::SourceImageRelationRule() : m_PurposeTag(""), m_DisplayName("Abstract image to image relation"), m_SourceRole("derived data"), m_DestinationRole("source image") {}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag) : SourceImageRelationRule(purposeTag, purposeTag + " relation"){}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName) : SourceImageRelationRule( purposeTag, displayName, "derived data", "source image"){}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole) : m_PurposeTag(purposeTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){}; mitk::SourceImageRelationRule::DataRelationUIDVectorType mitk::SourceImageRelationRule::GetRelationUIDs_DataLayer(const IPropertyProvider* source, const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const { DataRelationUIDVectorType result; auto relevantIndicesAndRuleIDs = GetReferenceSequenceIndices(source, destination, instances_IDLayer); auto itemRIIRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem"); auto regEx = std::regex(itemRIIRegExStr); //workaround until T24729 is done. Please remove if T24728 is done auto keys = PropertyRelationRuleBase::GetPropertyKeys(source); //end workaround for T24729 for (const auto &indexNRule : relevantIndicesAndRuleIDs) { bool relationCoveredByRII = false; for (const auto& key : keys) { if (std::regex_match(key, regEx)) { auto sequItemProp = source->GetConstProperty(key); if (sequItemProp.IsNotNull() && sequItemProp->GetValueAsString() == std::to_string(indexNRule.first)) { relationCoveredByRII = true; auto instanceID = GetInstanceIDByPropertyName(key); auto ruleID = GetRuleIDByInstanceID(source, instanceID); if (this->IsSupportedRuleID(ruleID)) { result.emplace_back(this->GetRelationUIDByInstanceID(source, instanceID), ruleID); } } } } if (!relationCoveredByRII) { //the relation is not covered on the RII level, so we generate the property path to the DICOM source reference (this is done via //the SOP Instance UIDs which uniquely identify an DICOM IOD). We use this property path as relation UID because it is identifying //on the data level (even so not long term stable if relations with a lower index are removed). PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddSelection("2112", indexNRule.first).AddElement("0008").AddElement("1155"); std::string ruleID = ""; if (!indexNRule.second.empty()) { ruleID = this->GenerateRuleID(indexNRule.second); } result.emplace_back(PropertyKeyPathToPropertyName(referencedInstanceUIDs), ruleID); } } return result; }; std::vector > mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source, const IPropertyProvider * destination, InstanceIDVectorType ignoreInstances) const { std::vector > result; BaseProperty::ConstPointer destInstanceUIDProp; std::string destinationUID = ""; if (destination) { destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } auto identifiable = this->CastProviderAsIdentifiable(destination); if (identifiable) { destinationUID= identifiable->GetUID(); } } std::vector ignoreItemIndices; for (const auto& iID : ignoreInstances) { auto sourceImageRefPath = GetRootKeyPath().AddElement(iID).AddElement("SourceImageSequenceItem"); auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); if (imageRefProp.IsNotNull()) { ignoreItemIndices.emplace_back(imageRefProp->GetValueAsString()); } } PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = PropertyRelationRuleBase::GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (destination==nullptr || *refUIDProp == *destInstanceUIDProp) { auto currentKeyPath = PropertyNameToPropertyKeyPath(key); auto currentKeyPathSelection = currentKeyPath.GetNode(2).selection; auto finding = std::find(ignoreItemIndices.begin(), ignoreItemIndices.end(), std::to_string(currentKeyPathSelection)); if (finding == ignoreItemIndices.end()) { PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); std::string currentPurpose = ""; if (purposeProp.IsNotNull()) { currentPurpose = purposeProp->GetValueAsString(); } if (this->IsAbstract() || (purposeProp.IsNotNull() && currentPurpose == this->m_PurposeTag)) { result.emplace_back(currentKeyPathSelection, currentPurpose); } } } } } return result; }; /**This mutex is used to guard mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ namespace { std::mutex sequenceItemCreationLock; } mitk::PropertyKeyPath::ItemSelectionIndex mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem( IPropertyOwner *source) const { std::lock_guard guard(sequenceItemCreationLock); ////////////////////////////////////// // Get all existing sequence items std::vector instanceIDs; PropertyKeyPath::ItemSelectionIndex newID = 0; PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto regExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); auto regEx = std::regex(regExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = instanceIDs.back()+1; } ////////////////////////////////////// // reserve new ID PropertyKeyPath newSourceImageSequencePath; newSourceImageSequencePath.AddElement("DICOM").AddElement("0008").AddSelection("2112",newID).AddElement("0008").AddElement("1155"); auto newKey = PropertyKeyPathToPropertyName(newSourceImageSequencePath); source->SetProperty(newKey, mitk::TemporoSpatialStringProperty::New("reserved slot for source image sequence")); return newID; }; void mitk::SourceImageRelationRule::Connect_datalayer(IPropertyOwner * source, const IPropertyProvider * destination, const InstanceIDType & instanceID) const { auto destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008,0x0018)); auto destClassUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0016)); if (destInstanceUIDProp.IsNotNull() && destClassUIDProp.IsNotNull()) { auto existingRefs = this->GetReferenceSequenceIndices(source, destination); std::string newSelectionIndexStr; if (!existingRefs.empty()) { newSelectionIndexStr = std::to_string(existingRefs[0].first); } else { PropertyKeyPath::ItemSelectionIndex newSelectionIndex = CreateNewSourceImageSequenceItem(source); PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1155"); source->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), destInstanceUIDProp->Clone()); PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1150"); source->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), destClassUIDProp->Clone()); PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); source->SetProperty(PropertyKeyPathToPropertyName(purposePath), StringProperty::New(m_PurposeTag)); newSelectionIndexStr = std::to_string(newSelectionIndex); } auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); source->SetProperty(PropertyKeyPathToPropertyName(sourceImageRefPath), StringProperty::New(newSelectionIndexStr).GetPointer()); } else { MITK_DEBUG << "Cannot connect SourceImageRelationRule on data layer. Passed destination does not have properties for DICOM SOP Instance UIDs(0x0008, 0x0018) and DICOM SOP Class UID(0x0008, 0x0016)"; } }; void mitk::SourceImageRelationRule::Disconnect_datalayer(IPropertyOwner * source, const RelationUIDType & relationUID) const { std::string deletedImageRefSequenceIndexStr = ""; if (relationUID.find("DICOM") == 0) { //relation that is purly data based. auto currentKeyPath = PropertyNameToPropertyKeyPath(relationUID); deletedImageRefSequenceIndexStr = std::to_string(currentKeyPath.GetNode(2).selection); } else { auto sourceImageRefPath = GetRootKeyPath().AddElement(this->GetInstanceIDByRelationUID(source,relationUID)).AddElement("SourceImageSequenceItem"); auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); if (imageRefProp.IsNotNull()) { deletedImageRefSequenceIndexStr = imageRefProp->GetValueAsString(); } } if(!deletedImageRefSequenceIndexStr.empty()) { auto deletedImageRefSequenceIndex = std::stoull(deletedImageRefSequenceIndexStr); auto refs = GetReferenceSequenceIndices(source); std::sort(refs.begin(), refs.end()); for (auto refIndex : refs) { if (refIndex.first >= deletedImageRefSequenceIndex) { PropertyKeyPath refDICOMDataPath; refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex.first); auto prefix = PropertyKeyPathToPropertyName(refDICOMDataPath); PropertyKeyPath refRelDataPath = GetRootKeyPath().AddAnyElement().AddElement("SourceImageSequenceItem");; auto riiRegEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(prefix) == 0) { //its a relevant DICOM property delete or update if (refIndex.first != deletedImageRefSequenceIndex) { //reindex to close the gap in the dicom sequence. auto newPath = PropertyNameToPropertyKeyPath(key); newPath.GetNode(2).selection = refIndex.first - 1; source->SetProperty(PropertyKeyPathToPropertyName(newPath), source->GetNonConstProperty(key)); } //remove old/outdated data layer information source->RemoveProperty(key); } if (std::regex_match(key, riiRegEx)) { //it is a relevant RII property, remove it or update it. auto imageSequenceItemProp = source->GetConstProperty(key); if (imageSequenceItemProp->GetValueAsString() == deletedImageRefSequenceIndexStr) { source->RemoveProperty(key); } else if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex.first)) { //its a relevant data property of the relation rule reindex it. source->SetProperty(key, StringProperty::New(std::to_string(refIndex.first - 1))); } } } } } } }; itk::LightObject::Pointer mitk::SourceImageRelationRule::InternalClone() const { itk::LightObject::Pointer result = Self::New(this->m_PurposeTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer(); return result; }; diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 4e2a3363d9..54bd9a77b8 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -1,357 +1,365 @@ /*============================================================================ 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 "mitkCoreActivator.h" #include #include // File IO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkLegacyFileWriterService.h" #include #include #include +// PropertyRelationRules +#include + // Micro Services #include #include #include #include #include #include #include #include #include #include // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). // To ensure that the code is called *before* the CppMicroServices // static initialization code (which triggers the Activator::Start // method), we include the ITK header here. #include void HandleMicroServicesMessages(us::MsgType type, const char *msg) { switch (type) { case us::DebugMsg: MITK_DEBUG << msg; break; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; case us::ErrorMsg: MITK_ERROR << msg; break; } } void AddMitkAutoLoadPaths(const std::string &programPath) { us::ModuleSettings::AddAutoLoadPath(programPath); #ifdef __APPLE__ // Walk up three directories since that is where the .dylib files are located // for build trees. std::string additionalPath = programPath; bool addPath = true; for (int i = 0; i < 3; ++i) { std::size_t index = additionalPath.find_last_of('/'); if (index != std::string::npos) { additionalPath = additionalPath.substr(0, index); } else { addPath = false; break; } } if (addPath) { us::ModuleSettings::AddAutoLoadPath(additionalPath); } #endif } void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) { mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); auto info = mitk::PropertyPersistenceInfo::New(); if (propPath.IsExplicit()) { std::string name = mitk::PropertyKeyPathToPropertyName(propPath); std::string key = name; std::replace(key.begin(), key.end(), '.', '_'); info->SetNameAndKey(name, key); } else { std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); } persistenceService->AddInfo(info); } class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) bool SupportsDimension(unsigned long dim) override { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext *context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } // m_RenderingManager = mitk::RenderingManager::New(); // context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService(m_PropertyFilters.get()); m_PropertyPersistence.reset(new mitk::PropertyPersistence); context->RegisterService(m_PropertyPersistence.get()); m_PropertyRelations.reset(new mitk::PropertyRelations); context->RegisterService(m_PropertyRelations.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); m_FileReaders.push_back(new mitk::GeometryDataReaderService()); m_FileWriters.push_back(new mitk::GeometryDataWriterService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); //add properties that should be persistent (if possible/supported by the writer) AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_VERSION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_OPTIONS_ANY()); + AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath()); + AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath()); + AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath()); + AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("","").AddAnyElement()); + /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); } void MitkCoreActivator::Unload(us::ModuleContext *) { for (auto &elem : m_FileReaders) { delete elem; } for (auto &elem : m_FileWriters) { delete elem; } for (auto &elem : m_FileIOs) { delete elem; } for (auto &elem : m_LegacyWriters) { delete elem; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); for (std::vector::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { delete *mimeTypeIter; } } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types std::vector mimeTypes = mitk::IOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for (auto &allobject : allobjects) { auto *io = dynamic_cast(allobject.GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast(io)) continue; // Use a custom mime-type for GDCMImageIO below if (dynamic_cast(allobject.GetPointer())) { // MITK provides its own DICOM reader (which internally uses GDCMImageIO). continue; } if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << (allobject)->GetNameOfClass(); } } FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); mitk::ItkImageIO *niftiIO = new mitk::ItkImageIO(mitk::IOMimeTypes::NIFTI_MIMETYPE(), itkNiftiIO.GetPointer(), 0); m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast(i->GetPointer()); if (io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Writer"; mitk::IFileWriter *writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). US_INITIALIZE_MODULE diff --git a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp index 1be7a6b47e..a4cd0f314c 100644 --- a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp +++ b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp @@ -1,1101 +1,1146 @@ /*============================================================================ 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 "mitkPropertyRelationRuleBase.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include /** This class is used to test PropertyRelationRuleBase and get access to internals where needed to test them as well. */ namespace mitk { class TestRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(TestRule, mitk::PropertyRelationRuleBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; RuleIDType GetRuleID() const override { if (m_AbstractMode) { return "TestRule"; } else { return "TestRule_type1"; } }; std::string GetDisplayName() const override { return "TestDisplayName"; } std::string GetSourceRoleName() const override { return "source role"; } std::string GetDestinationRoleName() const override { return "destination role"; } bool m_AbstractMode; bool IsAbstract() const override { return m_AbstractMode; } using Superclass::GetRootKeyPath; using Superclass::Connect; protected: TestRule() : m_AbstractMode(false) { }; ~TestRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; bool IsSupportedRuleID(const RuleIDType& ruleID) const override { if (m_AbstractMode) { return ruleID.find(this->GetRuleID()) == 0; } else { return Superclass::IsSupportedRuleID(ruleID); } }; DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override { DataRelationUIDVectorType result; mitk::BaseProperty::ConstPointer destProp; if (destination != nullptr) { destProp = destination->GetConstProperty("name"); } if (destProp.IsNotNull()) { auto destRegExStr = PropertyKeyPathToPropertyRegEx(Superclass::GetRootKeyPath().AddAnyElement().AddElement("dataHandle")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto keys = source->GetPropertyKeys(); for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto sourceProp = source->GetConstProperty(key); if (sourceProp->GetValueAsString() == destProp->GetValueAsString()) { if (instance_matches.size()>1) { auto finding = std::find(instances_IDLayer.begin(), instances_IDLayer.end(), instance_matches[1]); if (finding == instances_IDLayer.end()) { result.emplace_back(this->GetRelationUIDByInstanceID(source, instance_matches[1]), this->GetRuleIDByInstanceID(source, instance_matches[1])); } } } } } } if (result.empty() && instances_IDLayer.empty()) { auto refNameProp = source->GetConstProperty("referencedName"); if (refNameProp.IsNotNull() && (destProp.IsNull() || destProp->GetValueAsString() == refNameProp->GetValueAsString())) { result.emplace_back(refNameProp->GetValueAsString(),""); } } return result; }; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override { auto destProp = destination->GetConstProperty("name"); source->SetProperty("referencedName", StringProperty::New(destProp->GetValueAsString())); source->SetProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle")), StringProperty::New(destProp->GetValueAsString())); }; void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const override { source->RemoveProperty("referencedName"); try { auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); source->RemoveProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle"))); } catch(...) { } }; }; } // namespace mitk class mitkPropertyRelationRuleBaseTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyRelationRuleBaseTestSuite); MITK_TEST(GetRootKeyPath); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Disconnect_partial_ID); MITK_TEST(Disconnect_partial_Data); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); + MITK_TEST(GetRIIPropertyKeyPath); CPPUNIT_TEST_SUITE_END(); private: mitk::TestRule::Pointer rule; mitk::TestRule::Pointer abstractRule; mitk::DataNode::Pointer unRelated; mitk::PointSet::Pointer unRelated_1_data; mitk::DataNode::Pointer source_implicit_1; mitk::DataNode::Pointer source_data_1; mitk::DataNode::Pointer source_idOnly_1; mitk::DataNode::Pointer source_1; mitk::DataNode::Pointer source_multi; mitk::DataNode::Pointer source_otherRule; mitk::DataNode::Pointer source_otherTypeRule; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1; mitk::PointSet::Pointer dest_1_data; mitk::DataNode::Pointer dest_2; mitk::PointSet::Pointer dest_2_data; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } public: void setUp() override { rule = mitk::TestRule::New(); abstractRule = mitk::TestRule::New(); abstractRule->m_AbstractMode = true; unRelated = mitk::DataNode::New(); unRelated->SetName("unRelated"); unRelated_1_data = mitk::PointSet::New(); unRelated->SetData(unRelated_1_data); dest_1 = mitk::DataNode::New(); dest_1->SetName("dest_1"); dest_1_data = mitk::PointSet::New(); dest_1->SetData(dest_1_data); dest_2 = mitk::DataNode::New(); dest_2->SetName("dest_2"); dest_2_data = mitk::PointSet::New(); dest_2->SetData(dest_2_data); source_implicit_1 = mitk::DataNode::New(); source_implicit_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); source_idOnly_1 = mitk::DataNode::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_data_1 = mitk::DataNode::New(); source_data_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.dataHandle"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid10"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); source_1 = mitk::DataNode::New(); source_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid3"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.dataHandle"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid8"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); source_multi = mitk::DataNode::New(); source_multi->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid4"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.dataHandle"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.4.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid5")); name = "MITK.Relations.4.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.4.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid6")); name = "MITK.Relations.2.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("unknown")); name = "MITK.Relations.2.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_otherRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherTypeRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); } void tearDown() override {} void GetRootKeyPath() { mitk::PropertyKeyPath ref; ref.AddElement("MITK").AddElement("Relations"); CPPUNIT_ASSERT(mitk::PropertyRelationRuleBase::GetRootKeyPath() == ref); } void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(rule->IsSource(source_multi)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherTypeRule)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_multi)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherTypeRule)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated)); CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherTypeRule, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2)); CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated)); CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherTypeRule).empty()); auto uids = rule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); uids = rule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); uids = abstractRule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherTypeRule); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherTypeRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherTypeRule, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, dest_1).front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherTypeRule)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_2); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationDetector(source_multi, "uid5"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_implicit_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); // check upgrade of an data connection CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_data_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1_data->GetUID()); // check actualization of an id only connection CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_idOnly_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.dataHandle"; prop = source_idOnly_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information not stored.", prop->GetValueAsString() == dest_1->GetName()); prop = source_idOnly_1->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == dest_1->GetName()); // check actualization of an existing connection rule->Connect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check new connection auto newConnectUID = rule->Connect(source_multi, unRelated); CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated, mitk::PropertyRelationRuleBase::RelationType::Complete)); name = "MITK.Relations.5.dataHandle"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); prop = source_multi->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); name = "MITK.Relations.5.destinationUID"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated_1_data->GetUID()); auto storedRelationUIDs = rule->GetRelationUIDs(source_multi, unRelated); CPPUNIT_ASSERT_MESSAGE( "Relation uid was not stored for given source and destination.", storedRelationUIDs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Incorrect Relation uid was stored.", storedRelationUIDs[0] == newConnectUID); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.",this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_multi, dest_1); CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_otherTypeRule, "1")); } void Disconnect_partial_ID() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::ID), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::ID), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_1, "2")); } void Disconnect_partial_Data() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::Data), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::Data), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); abstractRule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_multi, dest_1); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherTypeRule, "1")); } + void GetRIIPropertyKeyPath() + { + auto path = mitk::PropertyRelationRuleBase::GetRootKeyPath(); + mitk::PropertyKeyPath referencePath({ "MITK", "Relations" }); + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("a","1"); + referencePath = { "MITK", "Relations", "1", "a" }; + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("", "1"); + referencePath = { "MITK", "Relations", "1"}; + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("a", ""); + referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("a"); + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath(); + referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("relationUID"); + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath("1"); + referencePath = { "MITK", "Relations", "1", "relationUID" }; + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath(); + referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("ruleID"); + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath("1"); + referencePath = { "MITK", "Relations", "1", "ruleID" }; + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath(); + referencePath = mitk::PropertyRelationRuleBase::GetRootKeyPath().AddAnyElement().AddElement("destinationUID"); + CPPUNIT_ASSERT(referencePath == path); + + path = mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath("1"); + referencePath = { "MITK", "Relations", "1", "destinationUID" }; + CPPUNIT_ASSERT(referencePath == path); + } + + }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyRelationRuleBase) diff --git a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp index 006153952b..71ac772ba4 100644 --- a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp +++ b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp @@ -1,944 +1,973 @@ /*============================================================================ 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 "mitkSourceImageRelationRule.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkPropertyNameHelper.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkPropertyNameHelper.h" #include class mitkSourceImageRelationRuleTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSourceImageRelationRuleTestSuite); + MITK_TEST(ConstructionAndGetter); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::SourceImageRelationRule::Pointer rule; mitk::SourceImageRelationRule::Pointer abstractRule; mitk::Image::Pointer unRelated; mitk::DataNode::Pointer unRelated_Node; mitk::Image::Pointer source_implicit_1; mitk::DataNode::Pointer source_implicit_1_Node; mitk::Image::Pointer source_Data_1; mitk::DataNode::Pointer source_Data_1_Node; mitk::Image::Pointer source_idOnly_1; mitk::DataNode::Pointer source_idOnly_1_Node; mitk::Image::Pointer source_1; mitk::DataNode::Pointer source_1_Node; mitk::Image::Pointer source_otherRule; mitk::DataNode::Pointer source_otherRule_Node; mitk::Image::Pointer source_otherPurpose; mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1_Node; mitk::Image::Pointer dest_1; mitk::DataNode::Pointer dest_2_Node; mitk::Image::Pointer dest_2; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } std::vector GetReferenceSequenceIndices(const mitk::IPropertyProvider * source, const mitk::IPropertyProvider * destination) const { std::vector result; auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } mitk::PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = source->GetPropertyKeys(); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (*refUIDProp == *destInstanceUIDProp) { mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key); result.push_back(std::to_string(finding.GetNode(2).selection)); } } } return result; }; void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID)); mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID)); mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose)); } bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath)); if (prop->GetValueAsString() != instanceUID) { return false; } mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath)); if (prop->GetValueAsString() != classUID) { return false; } mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); if (prop->GetValueAsString() != purpose) { return false; } return true; } public: void setUp() override { auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018); auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016); rule = mitk::SourceImageRelationRule::New("Test"); abstractRule = mitk::SourceImageRelationRule::New(); unRelated = mitk::Image::New(); unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated")); unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); unRelated_Node = mitk::DataNode::New(); unRelated_Node->SetData(unRelated); dest_1_Node = mitk::DataNode::New(); dest_1_Node->SetName("dest_1"); dest_1 = mitk::Image::New(); dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1")); dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_1_Node->SetData(dest_1); dest_2_Node = mitk::DataNode::New(); dest_2_Node->SetName("dest_2"); dest_2 = mitk::Image::New(); dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2")); dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_2_Node->SetData(dest_2); source_implicit_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Test", 0); source_implicit_1_Node = mitk::DataNode::New(); source_implicit_1_Node->SetData(source_implicit_1); source_idOnly_1 = mitk::Image::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_idOnly_1_Node = mitk::DataNode::New(); source_idOnly_1_Node->SetData(source_idOnly_1); source_Data_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Test", 0); SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.ruleID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); name = "MITK.Relations.2.ruleID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_Data_1_Node = mitk::DataNode::New(); source_Data_1_Node->SetData(source_Data_1); source_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_1, "dest_1", "image", "Test", 0); SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3")); name = "MITK.Relations.1.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8")); name = "MITK.Relations.2.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); source_1_Node = mitk::DataNode::New(); source_1_Node->SetData(source_1); source_otherRule = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherRule_Node = mitk::DataNode::New(); source_otherRule_Node->SetData(source_otherRule); source_otherPurpose = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_otherPurpose_Node = mitk::DataNode::New(); source_otherPurpose_Node->SetData(source_otherPurpose); } void tearDown() override {} + void ConstructionAndGetter() + { + CPPUNIT_ASSERT(abstractRule->IsAbstract()); + CPPUNIT_ASSERT(!rule->IsAbstract()); + + CPPUNIT_ASSERT_EQUAL(abstractRule->GetRuleID(), std::string("SourceImageRelation")); + CPPUNIT_ASSERT_EQUAL(abstractRule->GetDisplayName(), std::string("Abstract image to image relation")); + CPPUNIT_ASSERT_EQUAL(abstractRule->GetSourceRoleName(), std::string("derived data")); + CPPUNIT_ASSERT_EQUAL(abstractRule->GetDestinationRoleName(), std::string("source image")); + + CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Test")); + CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("Test relation")); + CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("derived data")); + CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image")); + + rule = mitk::SourceImageRelationRule::New("Test2", "DisplayName2"); + CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Test2")); + CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("DisplayName2")); + CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("derived data")); + CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image")); + + rule = mitk::SourceImageRelationRule::New("Test3", "DisplayName3", "s", "d"); + CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Test3")); + CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("DisplayName3")); + CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("s")); + CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("d")); + } + void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node)); CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1)); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_Data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose_Node)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty()); auto uids = rule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); uids = abstractRule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherPurpose); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherPurpose, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherPurpose_Node)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_implicit_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Test", 0)); // check upgrade and reuse of an Data connection (no new relation should be generated). CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_Data_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(relUID.size() == 1); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Test", 0)); // check actualization of an id only connection rule->Connect(source_idOnly_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check actualization of an existing connection rule->Connect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); // check creation of an new connection rule->Connect(unRelated, dest_1); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.", rule->GetExistingRelations(unRelated).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); // check creation of a 2nd connection of the same purpose rule->Connect(unRelated, dest_2); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.", rule->GetExistingRelations(unRelated).size() == 2); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); name = "MITK.Relations.2.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID()); name = "MITK.Relations.2.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.2.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Test", 1)); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); //check if index correction is correct, when disconnecting rule->Connect(source_1, dest_2); rule->Connect(source_1, unRelated); rule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4")); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Test", 2)); std::string name = "MITK.Relations.4.destinationUID"; auto prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID()); name = "MITK.Relations.4.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2"); rule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1")); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); abstractRule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherPurpose, "1")); } }; MITK_TEST_SUITE_REGISTRATION(mitkSourceImageRelationRule) diff --git a/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp b/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp index 6b8c472847..689ccf67c2 100644 --- a/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp +++ b/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp @@ -1,199 +1,210 @@ /*============================================================================ 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 "mitkDICOMTagsOfInterestHelper.h" #include #include "usModuleContext.h" #include "usGetModuleContext.h" mitk::DICOMTagPathMapType::value_type MakeLegacyEntry(const std::string& propName, const mitk::DICOMTag& tag) { return std::make_pair(tag, propName); } mitk::DICOMTagPathMapType::value_type MakeEntry(const mitk::DICOMTagPath& tagPath) { return std::make_pair(tagPath, ""); } mitk::DICOMTagPathMapType mitk::GetCurrentDICOMTagsOfInterest() { mitk::DICOMTagPathMapType result; std::vector > toiRegisters = us::GetModuleContext()->GetServiceReferences(); if (toiRegisters.empty()) { // bad, no service found, cannot get tags of interest MITK_ERROR << "DICOM tag error: no service for DICOM tags of interest"; return result; } else if (toiRegisters.size() > 1) { MITK_WARN << "DICOM tag error: multiple service for DICOM tags of interest found. Using just one."; } auto* toiRegister = us::GetModuleContext()->GetService(toiRegisters.front()); if (!toiRegister) { MITK_ERROR << "Service lookup error, cannot get DICOM tag of interest service "; } return toiRegister->GetTagsOfInterest(); } mitk::DICOMTagPathMapType mitk::GetDefaultDICOMTagsOfInterest() { DICOMTagPathMapType result; //These tags are copied from DICOMSeriesReader. The old naming style (deprecated) //is kept for backwards compatibility until it is removed. //Below we have also already added the properties with the new naming style // Patient module /*dicom.patient.PatientsName*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0010))); /*dicom.patient.PatientID*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0020))); /*dicom.patient.PatientsBirthDate*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0030))); /*dicom.patient.PatientsSex*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0040))); /*dicom.patient.PatientsBirthTime*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0032))); /*dicom.patient.OtherPatientIDs*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x1000))); /*dicom.patient.OtherPatientNames*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x1001))); /*dicom.patient.EthnicGroup*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x2160))); /*dicom.patient.PatientComments*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x4000))); /*dicom.patient.PatientIdentityRemoved*/ result.insert(MakeEntry(DICOMTag(0x0012, 0x0062))); /*dicom.patient.DeIdentificationMethod*/ result.insert(MakeEntry(DICOMTag(0x0012, 0x0063))); // General Study module /*dicom.study.StudyInstanceUID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x000d))); /*dicom.study.StudyDate*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0020))); /*dicom.study.StudyTime*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0030))); /*dicom.study.ReferringPhysiciansName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0090))); /*dicom.study.StudyID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0010))); /*dicom.study.AccessionNumber*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0050))); /*dicom.study.StudyDescription*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1030))); /*dicom.study.PhysiciansOfRecord*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1048))); /*dicom.study.NameOfPhysicianReadingStudy*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1060))); // General Series module /*dicom.series.Modality*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0060))); /*dicom.series.SeriesInstanceUID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x000e))); /*dicom.series.SeriesNumber*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0011))); /*dicom.series.Laterality*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0060))); /*dicom.series.SeriesDate*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0021))); /*dicom.series.SeriesTime*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0031))); /*dicom.series.PerformingPhysiciansName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1050))); /*dicom.series.ProtocolName*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x1030))); /*dicom.series.SeriesDescription*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x103e))); /*dicom.series.OperatorsName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1070))); /*dicom.series.BodyPartExamined*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x0015))); /*dicom.series.PatientPosition*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x5100))); /*dicom.series.SmallestPixelValueInSeries*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0108))); /*dicom.series.LargestPixelValueInSeries*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0109))); // VOI LUT module /*dicom.voilut.WindowCenter*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1050))); /*dicom.voilut.WindowWidth*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1051))); /*dicom.voilut.WindowCenterAndWidthExplanation*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1055))); // Image Pixel module /*dicom.pixel.PhotometricInterpretation*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0004))); /*dicom.pixel.Rows*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0010))); /*dicom.pixel.Columns*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0011))); // Image Plane module /*dicom.PixelSpacing*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0030))); /*dicom.ImagerPixelSpacing*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x1164))); //additional for RT /*dicom.RescaleIntercept*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1052))); /*dicom.RescaleSlope*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1053))); /*dicom.ManufacturerModelName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1090))); /*dicom.ManufacturerName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0070))); /*dicom.InstitutionName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0080))); /*dicom.StationName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1010))); /*dicom.DoseGridScaling*/ result.insert(MakeEntry(DICOMTag(0x3004, 0x000e))); //Additions for RTPLAN DICOMTagPath doseReferenceSequence; doseReferenceSequence.AddAnySelection(0x300A, 0x0010); DICOMTagPath fractionGroupSequence; fractionGroupSequence.AddAnySelection(0x300A, 0x0070); DICOMTagPath beamSequence; beamSequence.AddAnySelection(0x300A, 0x00B0); DICOMTagPath referencedStructureSetSequence; referencedStructureSetSequence.AddAnySelection(0x300C, 0x0060); result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0013))); //dicom.DoseReferenceSequence.DoseReferenceUID result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0016))); //dicom.DoseReferenceSequence.DoseReferenceDescription result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0026))); //dicom.DoseReferenceSequence.TargetPrescriptionDose result.insert(MakeEntry(DICOMTagPath(fractionGroupSequence).AddElement(0x300A, 0x0078))); //dicom.FractionGroupSequence.NumberOfFractionsPlanned result.insert(MakeEntry(DICOMTagPath(fractionGroupSequence).AddElement(0x300A, 0x0080))); //dicom.FractionGroupSequence.NumberOfBeams result.insert(MakeEntry(DICOMTagPath(beamSequence).AddElement(0x300A, 0x00C6))); //dicom.BeamSequence.RadiationType result.insert(MakeEntry(DICOMTagPath(referencedStructureSetSequence).AddElement(0x0008, 0x1155))); //dicom.ReferencedStructureSetSequence.ReferencedSOPInstanceUID //Additions for RTSTRUCT DICOMTagPath structureSetROISequence; structureSetROISequence.AddAnySelection(0x3006, 0x0020); result.insert(MakeEntry(DICOMTagPath(structureSetROISequence).AddElement(0x3006, 0x0022))); //dicom.StructureSetROISequence.ROINumber result.insert(MakeEntry(DICOMTagPath(structureSetROISequence).AddElement(0x3006, 0x0026))); //dicom.StructureSetROISequence.ROIName result.insert(MakeEntry(DICOMTagPath(structureSetROISequence).AddElement(0x3006, 0x0024))); //dicom.StructureSetROISequence.ReferencedFrameOfReferenceUID //Additions for RTDOSE DICOMTagPath planReferenceSequence; planReferenceSequence.AddAnySelection(0x300C, 0x0002); result.insert(MakeEntry(DICOMTagPath(planReferenceSequence).AddElement(0x0008, 0x1155))); //dicom.PlanReferenceSequence.ReferencedSOPInstanceUID //Additions for PET DICOMTagPath radioPharmaRootTag; radioPharmaRootTag.AddAnySelection(0x0054, 0x0016); DICOMTagPath radioNuclideRootTag(radioPharmaRootTag); radioNuclideRootTag.AddAnySelection(0x0054, 0x0300); result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x0031))); //dicom.pet.Radiopharmaceutical result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1072))); //dicom.pet.RadiopharmaceuticalStartTime result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1074))); //dicom.pet.RadionuclideTotalDose result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1075))); //dicom.pet.RadionuclideHalfLife result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1076))); //dicom.pet.RadionuclidePositronFraction result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0100))); //dicom.pet.Radionuclide.CodeValue result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0102))); //dicom.pet.Radionuclide.CodingSchemeDesignator - result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0104))); //dicom.pet.Radionuclide.CodemManing + result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0104))); //dicom.pet.Radionuclide.CodeMeaning result.insert(MakeEntry(DICOMTag(0x0054, 0x1001))); //dicom.pet.RadioactivityUnits result.insert(MakeEntry(DICOMTag(0x0054, 0x1102))); //dicom.pet.DecayCorrection result.insert(MakeEntry(DICOMTag(0x0054, 0x1321))); //dicom.pet.DecayFactor result.insert(MakeEntry(DICOMTag(0x0054, 0x1300))); //dicom.pet.FrameReferenceTime result.insert(MakeEntry(DICOMTag(0x0010, 0x1030))); //dicom.patient.PatientWeight result.insert(MakeEntry(DICOMTag(0x0010, 0x1020))); //dicom.patient.PatientSize //Other interesting acquisition correlated information result.insert(MakeEntry(DICOMTag(0x0008, 0x0022))); //dicom.acquisition date result.insert(MakeEntry(DICOMTag(0x0008, 0x0032))); //dicom.acquisition time result.insert(MakeEntry(DICOMTag(0x0008, 0x002a))); //dicom.acquisition datetime result.insert(MakeEntry(DICOMTag(0x0008, 0x0080))); //dicom.Modality result.insert(MakeEntry(DICOMTag(0x0018, 0x002a))); //dicom.Sequence Name result.insert(MakeEntry(DICOMTag(0x0018, 0x0020))); //dicom.Scanning Sequence result.insert(MakeEntry(DICOMTag(0x0018, 0x0021))); //dicom.Sequence Variant result.insert(MakeEntry(DICOMTag(0x0018, 0x0080))); //dicom.TR result.insert(MakeEntry(DICOMTag(0x0018, 0x0081))); //dicom.TE result.insert(MakeEntry(DICOMTag(0x0018, 0x1310))); //dicom.Acquisition Matrix result.insert(MakeEntry(DICOMTag(0x0018, 0x0087))); //dicom.Magnetic Field Strength //SOP result.insert(MakeEntry(DICOMTag(0x0008, 0x0018))); //SOP Instance UID result.insert(MakeEntry(DICOMTag(0x0020, 0x0013))); //Instance number result.insert(MakeEntry(DICOMTag(0x0020, 0x1041))); //Slice location + //Tags that document relations to other elements + DICOMTagPath sourceImageRefRootTag; + sourceImageRefRootTag.AddAnySelection(0x0008, 0x2112); + result.insert(MakeEntry(DICOMTagPath(sourceImageRefRootTag).AddElement(0x0008, 0x1155))); //dicom.SourceImage.ReferenceSOPInstanceUID + result.insert(MakeEntry(DICOMTagPath(sourceImageRefRootTag).AddElement(0x0008, 0x1150))); //dicom.SourceImage.ReferenceSOPClassUID + DICOMTagPath sourceImageRefPurposeRootTag(sourceImageRefRootTag); + sourceImageRefPurposeRootTag.AddAnySelection(0x0040, 0xa170); + result.insert(MakeEntry(DICOMTagPath(sourceImageRefPurposeRootTag).AddElement(0x0008, 0x0104))); //dicom.SourceImage.Purpose.CodeMeaning + result.insert(MakeEntry(DICOMTagPath(sourceImageRefPurposeRootTag).AddElement(0x0008, 0x0100))); //dicom.SourceImage.Purpose.CodeValue + result.insert(MakeEntry(DICOMTagPath(sourceImageRefPurposeRootTag).AddElement(0x0008, 0x0102))); //dicom.SourceImage.Purpose.CodeSchemeDesignator + return result; }; diff --git a/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp b/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp index 1c7e72b7eb..66eafb2386 100644 --- a/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp +++ b/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp @@ -1,100 +1,99 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include namespace mitk { /* * This is the module activator for the IO aspects of the "ModelFit" module. */ class ModelFitIOActivator : public us::ModuleActivator { public: void registerProperty(const std::string& name, const std::string& key, const std::string& description) { mitk::CoreServicePointer propDescService(mitk::CoreServices::GetPropertyDescriptions()); propDescService->AddDescription(name, description); mitk::PropertyPersistenceInfo::Pointer ppi = mitk::PropertyPersistenceInfo::New(); ppi->SetNameAndKey(name, key); mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); propPersistenceService->AddInfo(ppi, true); } void registerProperty(const std::string& name, const std::string& key, const std::string& description, const PropertyPersistenceInfo::DeserializationFunctionType &deFnc, const PropertyPersistenceInfo::SerializationFunctionType &serFnc) { mitk::CoreServicePointer propDescService(mitk::CoreServices::GetPropertyDescriptions()); propDescService->AddDescription(name, description); mitk::PropertyPersistenceInfo::Pointer ppi = mitk::PropertyPersistenceInfo::New(); ppi->SetNameAndKey(name, key); ppi->SetDeserializationFunction(deFnc); ppi->SetSerializationFunction(serFnc); mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); propPersistenceService->AddInfo(ppi, true); } void Load(us::ModuleContext* /*context*/) override { //register relevant properties - registerProperty(mitk::ModelFitConstants::UID_PROPERTY_NAME(), "data_uid", "UID used to identify data in an MITK session."); - registerProperty(mitk::ModelFitConstants::INPUT_VARIABLES_PROPERTY_NAME(), "modelfit_input_variables", "Array of input variables used in/for a model fit.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML); registerProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME(), "modelfit_parameter_name", "Name of the parameter, that is represented by the data and how it is used in the function string (modelfit.model.function)."); registerProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME(), "modelfit_parameter_unit", "Unit string of the Parameter. Is only used for display. Default value: \"\" (no unit)"); registerProperty(mitk::ModelFitConstants::PARAMETER_SCALE_PROPERTY_NAME(), "modelfit_parameter_scale", "Scaling of the parameter. Default value: 1."); registerProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME(), "modelfit_parameter_type", "Type of the parameters. Default value: parameter. Other options: derived, criterion, evaluation."); registerProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME(), "modelfit_model_type", "Value specifies the type of model (helpfull for classification; e.g. MR perfusion)"); registerProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME(), "modelfit_model_name", "Name of the specific fit. Only used for display."); - registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME(), "modelfit_model_function", "Functions string, that specifies the model and will be parsed, to plot the curves based on the parameter. Optional parameter that must not be set."); + registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME(), "modelfit_model_function", "Function string, that specifies the model and will be parsed, to plot the curves based on the parameter. Optional parameter that must not be set."); registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME(), "modelfit_model_functionClass", "ID of the model class implementation."); - registerProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME(), "modelfit_model_x", "Value identifies the model type."); + registerProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME(), "modelfit_model_x", "Name of the forumar parameter 'x', that is specified on the x axis. Only needed if model function string is set and formular must be parsed."); - registerProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME(), "modelfit_xaxis_name", "Value identifies the model type."); - registerProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME(), "modelfit_xaxis_unit", "Value identifies the model type."); + registerProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME(), "modelfit_xaxis_name", "Display name of the x axis."); + registerProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME(), "modelfit_xaxis_unit", "Unit of the x axis."); - registerProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME(), "modelfit_yaxis_name", "Value identifies the model type."); - registerProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME(), "modelfit_yaxis_unit", "Value identifies the model type."); + registerProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME(), "modelfit_yaxis_name", "Display name of the y axis."); + registerProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME(), "modelfit_yaxis_unit", "Unit of the y axis."); - registerProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME(), "modelfit_fit_uid", "Value identifies the model type."); + registerProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME(), "modelfit_fit_uid", "UID of the fit."); registerProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME(), "modelfit_fit_name", "Human readable name for the fit."); - registerProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME(), "modelfit_fit_type", "Value identifies the model type."); - registerProperty(mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME(), "modelfit_fit_input_imageUID", "Value identifies the model type."); - registerProperty(mitk::ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME(), "modelfit_fit_input_roiUID", "Value identifies the model type."); - registerProperty(mitk::ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME(), "modelfit_fit_input_data", "Value identifies the model type."); - registerProperty(mitk::ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME(), "modelfit_fit_staticParameters", "Value identifies the model type.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML); + registerProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME(), "modelfit_fit_type", "Type of the fit (e.g. ROI based or pixel based)."); + registerProperty(mitk::ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME(), "modelfit_fit_input_roiUID", "UID of the ROI used for the fit."); + registerProperty(mitk::ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME(), "modelfit_fit_input_data", "Property containing input data directly stored in the fit information."); + registerProperty(mitk::ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME(), "modelfit_fit_staticParameters", "Property containing static parameters used for the fit.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML); + + //legacy properties for backwards compatibility + registerProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME(), "data_uid", "UID used to identify data in an MITK session."); + registerProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME(), "modelfit_fit_input_imageUID", "UID of the input image that used to fit the model."); } void Unload(us::ModuleContext* ) override { } -private: - }; } US_EXPORT_MODULE_ACTIVATOR(mitk::ModelFitIOActivator) diff --git a/Modules/ModelFit/files.cmake b/Modules/ModelFit/files.cmake index 40d7ada4fc..85f16dc618 100644 --- a/Modules/ModelFit/files.cmake +++ b/Modules/ModelFit/files.cmake @@ -1,76 +1,76 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES Common/mitkExtractTimeGrid.cpp Common/mitkTimeGridHelper.cpp Common/mitkMaskedDynamicImageStatisticsGenerator.cpp Common/mitkModelFitConstants.cpp Common/mitkModelFitParameter.cpp Common/mitkModelFitCmdAppsHelper.cpp Common/mitkParameterFitImageGeneratorBase.cpp Common/mitkPixelBasedParameterFitImageGenerator.cpp Common/mitkROIBasedParameterFitImageGenerator.cpp Common/mitkModelFitInfo.cpp Common/mitkModelFitStaticParameterMap.cpp Common/mitkModelGenerator.cpp - Common/mitkModelFitUIDHelper.cpp Common/mitkModelFitResultHelper.cpp Common/mitkScalarListLookupTable.cpp Common/mitkScalarListLookupTableProperty.cpp Common/mitkScalarListLookupTablePropertySerializer.cpp Common/mitkIModelFitProvider.cpp Common/mitkModelFitParameterValueExtraction.cpp Common/mitkBinaryImageToLabelSetImageFilter.cpp Common/mitkFormulaParser.cpp Common/mitkFresnel.cpp Common/mitkModelFitPlotDataHelper.cpp Common/mitkModelSignalImageGenerator.cpp + Common/mitkModelFitResultRelationRule.cpp Functors/mitkSimpleFunctorBase.cpp Functors/mitkSimpleFunctorPolicy.cpp Functors/mitkChiSquareFitCostFunction.cpp Functors/mitkReducedChiSquareFitCostFunction.cpp Functors/mitkConstraintCheckerBase.cpp Functors/mitkSimpleBarrierConstraintChecker.cpp Functors/mitkSquaredDifferencesFitCostFunction.cpp Functors/mitkSumOfSquaredDifferencesFitCostFunction.cpp Functors/mitkMVConstrainedCostFunctionDecorator.cpp Functors/mitkMVModelFitCostFunction.cpp Functors/mitkNormalizedSumOfSquaredDifferencesFitCostFunction.cpp Functors/mitkSVModelFitCostFunction.cpp Functors/mitkModelFitFunctorBase.cpp Functors/mitkLevenbergMarquardtModelFitFunctor.cpp Functors/mitkDummyModelFitFunctor.cpp Functors/mitkModelFitInfoSignalGenerationFunctor.cpp Functors/mitkIndexedValueFunctorPolicy.cpp Functors/mitkModelDataGenerationFunctor.cpp Models/mitkModelBase.cpp Models/mitkModelFactoryBase.cpp Models/mitkModelParameterizerBase.cpp Models/mitkLinearModel.cpp Models/mitkLinearModelFactory.cpp Models/mitkInitialParameterizationDelegateBase.cpp Models/mitkImageBasedParameterizationDelegate.cpp Models/mitkGenericParamModel.cpp Models/mitkGenericParamModelFactory.cpp Models/mitkGenericParamModelParameterizer.cpp Models/mitkValueBasedParameterizationDelegate.cpp Models/mitkT2DecayModel.cpp Models/mitkT2DecayModelFactory.cpp Models/mitkT2DecayModelParameterizer.cpp TestingHelper/mitkTestModel.cpp TestingHelper/mitkTestModelFactory.cpp ) set(TPP_FILES include/itkMultiOutputNaryFunctorImageFilter.tpp include/itkMaskedStatisticsImageFilter.hxx include/itkMaskedNaryStatisticsImageFilter.hxx include/mitkModelFitProviderBase.tpp ) set(HXX_FILES ) set(MOC_H_FILES ) diff --git a/Modules/ModelFit/include/mitkModelFitConstants.h b/Modules/ModelFit/include/mitkModelFitConstants.h index cd46c07776..c211bb51b7 100644 --- a/Modules/ModelFit/include/mitkModelFitConstants.h +++ b/Modules/ModelFit/include/mitkModelFitConstants.h @@ -1,197 +1,202 @@ /*============================================================================ 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 _MITK_MODEL_FIT_CONSTANTS_H_ #define _MITK_MODEL_FIT_CONSTANTS_H_ #include #include "MitkModelFitExports.h" namespace mitk { struct MITKMODELFIT_EXPORT ModelFitConstants { /** * Name of the "root" property for all information concerning model fitting. */ static const std::string MODEL_FIT_PROPERTY_NAME(); - /** - * Name of the "uid" property for all data objects concerning model fitting. - */ - static const std::string UID_PROPERTY_NAME(); - /** * modelfit.input.variables ist eine Map der Variablennamen, die einem Eingabebild zu eigen sind und * unabhängig von einem Fit sind. * Deren Wert ist ein Array, welches die Variablenwerte für die einzelnen Zeitschritte enthält. * Die Länge eines solchen Arrays muss entweder der Anzahl der Zeitschritte dieses Bildes entsprechen * oder genau 1 sein, wenn der Wert für alle Zeitschritte dieses Bildes gleich ist. * Beispiele: * ["x1":[580], "x2":[3060], "x3":[41]] (1 Zeitschritt, 3 Variablen) * ["TE":[2.47, 5.85, 9.23, 12.61], "TI":[10.3]] (4 Zeitschritte, 2 Variablen) */ static const std::string INPUT_VARIABLES_PROPERTY_NAME(); /** * modelfit.parameter.name ist der Bezeichner des Parameters, wie es im Funktions-String (modelfit.model.function) vorkommt. */ static const std::string PARAMETER_NAME_PROPERTY_NAME(); /** * modelfit.parameter.unit ist die Einheit des Parameters und dient nur der formatierten Ausgabe. Standardwert: "" */ static const std::string PARAMETER_UNIT_PROPERTY_NAME(); /** * modelfit.parameter.scale ist die Skalierung des Parameters. Standardwert: 1 */ static const std::string PARAMETER_SCALE_PROPERTY_NAME(); /** * modelfit.parameter.type ist der type des parameters. Standardwert: PARAMETER_TYPE_VALUE_PARAMETER */ static const std::string PARAMETER_TYPE_PROPERTY_NAME(); /** * modelfit.parameter.type Wert für normale Parameters. */ static const std::string PARAMETER_TYPE_VALUE_PARAMETER(); /** * modelfit.parameter.type Wert für derived Parameters. */ static const std::string PARAMETER_TYPE_VALUE_DERIVED_PARAMETER(); /** * modelfit.parameter.type Wert für Crtierion-Parameters. */ static const std::string PARAMETER_TYPE_VALUE_CRITERION(); /** * modelfit.parameter.type Wert für Evaluation-Parameters. */ static const std::string PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER(); /** * modelfit.model.type ist der Bezeichner des Modelltyps und dient nur der formatierten Ausgabe */ static const std::string MODEL_TYPE_PROPERTY_NAME(); /** * modelfit.model.name ist der Bezeichner des Fits und dient nur der formatierten Ausgabe. */ static const std::string MODEL_NAME_PROPERTY_NAME(); /** * modelfit.model.function ist der Funktions-String, der geparsed wird, um die Kurve zu plotten. */ static const std::string MODEL_FUNCTION_PROPERTY_NAME(); /** * modelfit.model.functionClass ist die ID der Modellklasse. */ static const std::string MODEL_FUNCTION_CLASS_PROPERTY_NAME(); /** * modelfit.model.x ist der Name der Variable, die die x-Werte stellt, wie sie im Funktions-String vorkommt. Wird nur gebraucht wenn modelfit.model.function definiert ist. */ static const std::string MODEL_X_PROPERTY_NAME(); /** * Default value for MODEL_X_PROPERTY_NAME. */ static const std::string MODEL_X_VALUE_DEFAULT(); /** * modelfit.xaxis.name ist der Bezeichner der x-Achse und dient nur der formatierten Ausgabe. Standardwert: "Time" */ static const std::string XAXIS_NAME_PROPERTY_NAME(); /** * Default value for XAXIS_NAME_PROPERTY_NAME. */ static const std::string XAXIS_NAME_VALUE_DEFAULT(); /** * modelfit.xaxis.unit ist die Einheit der x-Achse und dient nur der formatierten Ausgabe. Standardwert: "ms" */ static const std::string XAXIS_UNIT_PROPERTY_NAME(); /** * modelfit.xaxis.name ist der Bezeichner der x-Achse und dient nur der formatierten Ausgabe. Standardwert: "Intensity" */ static const std::string YAXIS_NAME_PROPERTY_NAME(); /** * Default value for YAXIS_NAME_PROPERTY_NAME. */ static const std::string YAXIS_NAME_VALUE_DEFAULT(); /** * modelfit.xaxis.unit ist die Einheit der x-Achse und dient nur der formatierten Ausgabe. Standardwert: "" */ static const std::string YAXIS_UNIT_PROPERTY_NAME(); /** * modelfit.fit.uid ist eine einzigartige ID (unabhängig von Ausführungsrechner, Session oder Applikationsinstanz), * die einem Fit vergeben wird um alle zugehörigen Bilder eindeutig zu kennzeichnen. */ static const std::string FIT_UID_PROPERTY_NAME(); /** * modelfit.fit.name is the human readable name of the fit. Use UID, if you want a unique identifier. This is just * used for visualization purposes. */ static const std::string FIT_NAME_PROPERTY_NAME(); /** * modelfit.fit.type defines the type of model fitting; e.g. pixel based or ROI based. Thus it determines the meaning of other fit specific informations. */ static const std::string FIT_TYPE_PROPERTY_NAME(); static const std::string FIT_TYPE_VALUE_PIXELBASED(); - static const std::string FIT_TYPE_VALUE_ROIBASED(); - - /** - * modelfit.fit.input.imageUID defines the UID of the image that is used directly or indirectly (then it is source for input.data) to make the fit. - */ - static const std::string FIT_INPUT_IMAGEUID_PROPERTY_NAME(); + static const std::string FIT_TYPE_VALUE_ROIBASED(); /** * modelfit.fit.input.roiUID defines the UID of the ROI that is used to make the fit. If not set no Mask was used or specified. */ static const std::string FIT_INPUT_ROIUID_PROPERTY_NAME(); /** * modelfit.fit.input.data defines the data signal that is used to make the fit and was extracted from the input image (e.g. in ROIbased fit). */ static const std::string FIT_INPUT_DATA_PROPERTY_NAME(); /** * modelfit.fit.staticParameters ist eine Map der Variablennamen, die im Funktions-String vorkommen und * nicht mit modelfit.input.variables abgedeckt sind. Dies können z.B. Konstanten sein oder Variablen, * die sich (abhängig vom Fit) über den Zeitverlauf ändern, wie z.B. der Mittelwert einer Maske über einen Arterienquerschnitt. * (Entspricht den StaticParameters in mitk::ModelBase) * Der Wert der Variablen ist ein Array, welches die Variablenwerte enthält. * Beispiel: * ["AIF":[2, 8, 8, 4, 5], "tau":[0.42]] (insgesamt 5 Zeitschritte, 2 Variablen) */ static const std::string FIT_STATIC_PARAMETERS_PROPERTY_NAME(); + /** + * Name of the "uid" property for all data objects concerning model fitting. + * @remark This was also known as UID_PROPRTY_NAME(). It was renamed to indicate + * that is only kept for backwards compatibility. Model fit now uses the Identifiable + * interface of BaseData and the PropertyRelationRules to identify/find data. + */ + static const std::string LEGACY_UID_PROPERTY_NAME(); + + /** + * modelfit.fit.input.imageUID defines the UID of the image that is used directly or indirectly (then it is source for input.data) to make the fit. + * @remark This was also known as FIT_INPUT_IMAGEUID_PROPERTY_NAME(). It was renamed to indicate + * that is only kept for backwards compatibility. Model fit now uses the Identifiable + * interface of BaseData and the ModelFitResultRelationRule to identify/find the input data. + */ + static const std::string LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME(); }; } #endif diff --git a/Modules/ModelFit/include/mitkModelFitInfo.h b/Modules/ModelFit/include/mitkModelFitInfo.h index 8189181357..fd88278c91 100644 --- a/Modules/ModelFit/include/mitkModelFitInfo.h +++ b/Modules/ModelFit/include/mitkModelFitInfo.h @@ -1,202 +1,199 @@ /*============================================================================ 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 mitkModelFitInfo_h #define mitkModelFitInfo_h #include #include #include #include "mitkModelFitConstants.h" #include "mitkModelFitParameter.h" #include "mitkModelFitStaticParameterMap.h" #include "mitkScalarListLookupTable.h" -#include "mitkModelFitUIDHelper.h" #include "mitkModelParameterizerBase.h" #include "mitkModelTraitsInterface.h" #include "MitkModelFitExports.h" namespace mitk { namespace modelFit { /** * @brief Data class that stores all information about a modelfit that is relevant to the * visualization and stored as properties in the result nodes. */ class MITKMODELFIT_EXPORT ModelFitInfo : public itk::LightObject { public: - typedef mitk::NodeUIDType UIDType; + typedef std::string UIDType; typedef std::vector ParamListType; typedef ParamListType::const_iterator ConstIterType; mitkClassMacroItkParent(ModelFitInfo, itk::LightObject); itkSimpleNewMacro(ModelFitInfo); ModelFitInfo() : x(mitk::ModelFitConstants::MODEL_X_VALUE_DEFAULT()), xAxisName(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT()), yAxisName(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT()) { } /** * @brief Adds the given parameter to this fit's parameter list if it doesn't * exist already. * @param p The param that should be added to this fit's parameter list. */ void AddParameter(Parameter::Pointer p); /** * @brief Searches for the parameter with the given name and type in the fit's * parameter list and returns it. * @param name The name of the desired parameter. * @param type The type of the desired parameter. * @return The parameter with the given name on success or NULL otherwise. */ Parameter::ConstPointer GetParameter(const std::string& name, const Parameter::Type& type) const; /** * @brief Searches for the parameter with the given name and type in the fit's * parameter list and deletes it if it exists. * @param name The name of the desired parameter. * @param type The type of the desired parameter. */ void DeleteParameter(const std::string& name, const Parameter::Type& type); /**Return const reference to the parameter list.*/ const ParamListType& GetParameters() const; /** ModelFitConstants::MODEL_NAME_PROPERTY_NAME */ std::string modelName; /** ModelFitConstants::MODEL_TYPE_PROPERTY_NAME */ std::string modelType; /** ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME */ std::string function; /** ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME */ std::string functionClassID; /** ModelFitConstants::MODEL_X_PROPERTY_NAME */ std::string x; /** ModelFitConstants::XAXIS_NAME_PROPERTY_NAME */ std::string xAxisName; /** ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME */ std::string xAxisUnit; /** ModelFitConstants::YAXIS_NAME_PROPERTY_NAME */ std::string yAxisName; /** ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME */ std::string yAxisUnit; /** ModelFitConstants::FIT_UID_PROPERTY_NAME */ UIDType uid; /** ModelFitConstants::FIT_NAME_PROPERTY_NAME */ std::string fitName; /** ModelFitConstants::FIT_TYPE_PROPERTY_NAME */ std::string fitType; /** ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME */ StaticParameterMap staticParamMap; /** ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME */ UIDType roiUID; - /** ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME */ - UIDType inputUID; /** ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME */ ScalarListLookupTable inputData; - mitk::Image::Pointer inputImage; + mitk::Image::ConstPointer inputImage; private: typedef ParamListType::iterator IterType; typedef itk::MutexLockHolder LockType; ParamListType parameterList; itk::SimpleFastMutexLock mutex; }; /** * @brief Reads the string property with the given name from the data of the given node * and returns its value. Throws a ModelFitException if the property doesn't exist. * @param node The node whose property value should be returned. * @param prop The name of the property that should be read. * @return The value of the found property. * @throw ModelFitException If the property doesn't exist or returns an empty string. */ MITKMODELFIT_EXPORT const std::string GetMandatoryProperty(const mitk::DataNode* node, const std::string& prop); /** * @brief Reads the string property with the given name from the given base data and * returns its value. Throws a ModelFitException if the property doesn't exist. * @param data The data whose property value should be returned. * @param prop The name of the property that should be read. * @return The value of the found property. * @throw ModelFitException If the property doesn't exist or returns an empty string. */ MITKMODELFIT_EXPORT const std::string GetMandatoryProperty(const mitk::BaseData* data, const std::string& prop); /** * @brief Creates a new ModelFitInfo instance from the nodes in the passed storage. * The fit will be identified by the passed UID. Returns the instance on * success. * @param uid The uid of the fit that should get its ModelFitInfo created and which identifies the nodes in the storage. * @param storage Pointer to the data storage containing any potential relevantThe nodes. * @return The newly created modelfit on success or NULL otherwise. */ MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromNode(const ModelFitInfo::UIDType& uid, const mitk::DataStorage* storage); /** creates a new ModelFitInfo instance from a passed modal instance and his traits instance* * @param usedModel Pointer to a model which was used for a fit, which should get a fit info created. * @param modelTraits Pointer to traits interface for the model that was used for the fit. * @param inputImage Pointer to the input image. If it has no UID yet, a property will be added to the node. * @param fitType String identifying the type of the fit (e.g. ROI based or voxel based) * @param fitName Optional human readable name of the fit. * @param roiUID UID of the ROI, if one was used. * @return The newly created modelfit on success or NULL otherwise.*/ MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromModelParameterizer( const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage, - const std::string& fitType, const std::string& fitName = "", const NodeUIDType roiUID = ""); + const std::string& fitType, const std::string& fitName = "", const ModelFitInfo::UIDType& roiUID = ""); /** @overload Overloaded version that allows additional definition of optional input data for the fit.*/ MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromModelParameterizer( const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage, const std::string& fitType, const ScalarListLookupTable& inputData, const std::string& fitName = "", - const NodeUIDType roiUID = ""); + const ModelFitInfo::UIDType& roiUID = ""); /** Returns all nodes that belong to the fit indicated by the passed UID. * @param fitUID The uid of the fit that is relevant for the query. * @param storage Pointer to the data storage containing any potential relevant nodes. * @return The set of found nodes or null if storage is not valid. */ MITKMODELFIT_EXPORT DataStorage::SetOfObjects::ConstPointer GetNodesOfFit( const ModelFitInfo::UIDType& fitUID, const mitk::DataStorage* storage); typedef std::set NodeUIDSetType; /** Returns the UIDs of all fits that are derived (directly or indirectly from the passed node). * @param node The node which defines the parent node. It will be searched in his derived nodes for fits. * @param storage Pointer to the data storage containing any potential relevant nodes. * @return The set of found uid will be returned. */ MITKMODELFIT_EXPORT NodeUIDSetType GetFitUIDsOfNode(const mitk::DataNode* node, const mitk::DataStorage* storage); } } #endif // mitkModelFit_h diff --git a/Modules/ModelFit/include/mitkModelFitParameter.h b/Modules/ModelFit/include/mitkModelFitParameter.h index 14cf751133..e87b1845ae 100644 --- a/Modules/ModelFit/include/mitkModelFitParameter.h +++ b/Modules/ModelFit/include/mitkModelFitParameter.h @@ -1,66 +1,65 @@ /*============================================================================ 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 mitkModelFitParameter_h #define mitkModelFitParameter_h #include -#include "mitkModelFitUIDHelper.h" #include "MitkModelFitExports.h" namespace mitk { namespace modelFit { /** * @brief Data class that stores all information about a modelfit parameter. * Such a parameter is going to be fitted for the according model. */ class MITKMODELFIT_EXPORT Parameter : public itk::LightObject { public: mitkClassMacroItkParent(Parameter, itk::LightObject); itkSimpleNewMacro(Parameter); enum Type { ParameterType, DerivedType, CriterionType, EvaluationType }; Parameter(); /** ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME */ std::string name; /** ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME */ Type type; /** ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME */ std::string unit; /** ModelFitConstants::PARAMETER_SCALE_PROPERTY_NAME */ float scale; /** Corresponding image */ mitk::Image::ConstPointer image; }; /** Extracts the parameter information stored in the data instance and returns it as parameter instance. * If the data does not encode an parameter/fit result a Null pointer will be returned. * @param data The data instance that contains parameter information that should be extracted. * @return The newly created parameter instance on success or NULL otherwise. */ MITKMODELFIT_EXPORT Parameter::Pointer ExtractParameterFromData(const mitk::BaseData* data); } } #endif // mitkModelFitParameter_h diff --git a/Modules/ModelFit/include/mitkModelFitResultRelationRule.h b/Modules/ModelFit/include/mitkModelFitResultRelationRule.h new file mode 100644 index 0000000000..965ce514fd --- /dev/null +++ b/Modules/ModelFit/include/mitkModelFitResultRelationRule.h @@ -0,0 +1,73 @@ +/*============================================================================ + +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 MITK_MODELFIT_RESULT_RELATION_RULE_H +#define MITK_MODELFIT_RESULT_RELATION_RULE_H + +#include "mitkSourceImageRelationRule.h" + +#include "MitkModelFitExports.h" + +namespace mitk +{ + /**This rule class can be used to represent the relation between an image generated by a model fit (rule source) + and the image that was fitted (rule destination). + The addition to the behavirID-layer is supported like for SourceImageRelationRule. + In addition the rule uses the data-layer to deduce/define relations. + For this layer it uses properties compliant to DICOM. Thus (1) the information is stored in + a DICOM Source Image Sequence item (0x0008,0x2112) and (2) the destination must have properties + DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016). If the destination + does not have this properties, no connection can be made on the data-layer. + @remark Please note that PropertyRelationRules and DICOM use the term "source" differently. The DICOM + source (image) equals the PropertyRelationRule destination. This is due to + an inverted relation direction. So in the context of the SourceImageRelationRule interface a derived data is + the source and points to the original image, it derives from. In the context of DICOM this referenced original image would be + called source image (as the name of this class). + In order to be able to use this class for different relation types (DICOM would call it purposes), + the purposeTag is used. It must be specified when creating a rule instance. + The purposeTag will be used as suffix for the rule ID of the instance and therefore allows to create + specific and distinguishable rules instances based on this class. + One may also specify the display name and the role names of the instance. If not specified the default values + are used (display name: " relation", source role name: "derived data", + destination role name: "source image") + */ + class MITKMODELFIT_EXPORT ModelFitResultRelationRule : public mitk::SourceImageRelationRule + { + public: + mitkClassMacro(ModelFitResultRelationRule, SourceImageRelationRule); + itkNewMacro(Self); + + using RuleIDType = Superclass::RuleIDType; + using RelationUIDType = Superclass::RelationUIDType; + using RelationUIDVectorType = Superclass::RelationUIDVectorType; + + bool IsDestinationCandidate(const IPropertyProvider *owner) const override; + + protected: + ModelFitResultRelationRule(); + ~ModelFitResultRelationRule() override = default; + + using InstanceIDType = SourceImageRelationRule::InstanceIDType; + using InstanceIDVectorType = SourceImageRelationRule::InstanceIDVectorType; + + using DataRelationUIDVectorType = Superclass::DataRelationUIDVectorType; + virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; + + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; + + itk::LightObject::Pointer InternalClone() const override; + }; + +} // namespace mitk + +#endif diff --git a/Modules/ModelFit/include/mitkModelFitUIDHelper.h b/Modules/ModelFit/include/mitkModelFitUIDHelper.h deleted file mode 100644 index d601844ca3..0000000000 --- a/Modules/ModelFit/include/mitkModelFitUIDHelper.h +++ /dev/null @@ -1,55 +0,0 @@ -/*============================================================================ - -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 mitkModelFitUIDHelper_h -#define mitkModelFitUIDHelper_h - -#include - -//MITK -#include - -#include "MitkModelFitExports.h" - -namespace mitk -{ - class BaseData; - class DataStorage; - - typedef std::string NodeUIDType; - - /** Helper that searches for the node in the storage that has a data object with the given uid. Returns NULL if no node was found.*/ - DataNode::Pointer MITKMODELFIT_EXPORT GetNodeByModelFitUID(const mitk::DataStorage* storage, const NodeUIDType& uid); - - /** Gets the UID of the passed data. The UID is stored in the property "data.uid". If this property does not exist the method will - check the existance of a property for the DICOM tag series instance UID (0x0020, 0x000e) and return the value as UID. If this does also - not exist, a UID will generated, the property "data.uid" will be added with a new UID and the new UID will be returned. - @pre Passed data is a valid pointer.*/ - NodeUIDType MITKMODELFIT_EXPORT EnsureModelFitUID(mitk::BaseData* data); - /** Convinience function that calls the overloaded EnsureModelFitUID() with the data of the passed node. - @pre Passed node is a valid pointer.*/ - NodeUIDType MITKMODELFIT_EXPORT EnsureModelFitUID(mitk::DataNode* node); - - /** Helper that checks if the data has the passed UID. Following strategy will be used for the check. - 1. Checks existance of property "data.uid" and if its content equals the passed uid. - 2. If 1 fails, it checks the existance of a property for the DICOM tag series instance UID (0x0020, 0x000e) and the equality of its value. - 3. If 2 also fails, return will be false.*/ - bool MITKMODELFIT_EXPORT CheckModelFitUID(const mitk::BaseData* data, const NodeUIDType& uid); - /** Convinience helper function that calls the overloaded CheckModelFitUID() with the data of the passed node.*/ - bool MITKMODELFIT_EXPORT CheckModelFitUID(const mitk::DataNode* node, const NodeUIDType& uid); - - -} - -#endif - diff --git a/Modules/ModelFit/src/Common/mitkModelFitConstants.cpp b/Modules/ModelFit/src/Common/mitkModelFitConstants.cpp index df83f5463a..64a506c507 100644 --- a/Modules/ModelFit/src/Common/mitkModelFitConstants.cpp +++ b/Modules/ModelFit/src/Common/mitkModelFitConstants.cpp @@ -1,47 +1,47 @@ /*============================================================================ 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 "mitkModelFitConstants.h" const std::string mitk::ModelFitConstants::MODEL_FIT_PROPERTY_NAME() { return "modelfit";} -const std::string mitk::ModelFitConstants::UID_PROPERTY_NAME() { return "data.uid";} +const std::string mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME() { return "data.uid";} const std::string mitk::ModelFitConstants::INPUT_VARIABLES_PROPERTY_NAME() { return "modelfit.input.variables";} const std::string mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME() { return "modelfit.parameter.name";} const std::string mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME() { return "modelfit.parameter.type";} const std::string mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER() { return "parameter";} const std::string mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER() { return "derived";} const std::string mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_CRITERION() { return "criterion";} const std::string mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER() { return "evaluation";} const std::string mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME() { return "modelfit.parameter.unit";} const std::string mitk::ModelFitConstants::PARAMETER_SCALE_PROPERTY_NAME() { return "modelfit.parameter.scale";} const std::string mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME() { return "modelfit.model.type";} const std::string mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME() { return "modelfit.model.name";} const std::string mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME() { return "modelfit.model.function";} const std::string mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME() { return "modelfit.model.functionClass";} const std::string mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME() { return "modelfit.model.x";} const std::string mitk::ModelFitConstants::MODEL_X_VALUE_DEFAULT() { return "x";} const std::string mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME() { return "modelfit.xaxis.name";} const std::string mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() { return "Time";} const std::string mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME() { return "modelfit.xaxis.unit";} const std::string mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME() { return "modelfit.yaxis.name";} const std::string mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() { return "Intensity";} const std::string mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME() { return "modelfit.yaxis.unit";} const std::string mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME() { return "modelfit.fit.uid"; } const std::string mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME() { return "modelfit.fit.name"; } const std::string mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME() { return "modelfit.fit.type";} const std::string mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() { return "pixelbased";} const std::string mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED() { return "ROIbased";} -const std::string mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME() { return "modelfit.fit.input.imageUID";} +const std::string mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME() { return "modelfit.fit.input.imageUID";} const std::string mitk::ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME() { return "modelfit.fit.input.roiUID";} const std::string mitk::ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME() { return "modelfit.fit.input.data";} const std::string mitk::ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME() { return "modelfit.fit.staticParameters";} diff --git a/Modules/ModelFit/src/Common/mitkModelFitInfo.cpp b/Modules/ModelFit/src/Common/mitkModelFitInfo.cpp index 065868e971..995ff2ab01 100644 --- a/Modules/ModelFit/src/Common/mitkModelFitInfo.cpp +++ b/Modules/ModelFit/src/Common/mitkModelFitInfo.cpp @@ -1,429 +1,431 @@ /*============================================================================ 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 "mitkModelFitInfo.h" + #include +#include #include #include "mitkDataNode.h" #include "mitkDataStorage.h" -#include "mitkModelFitInfo.h" + #include "mitkScalarListLookupTableProperty.h" #include "mitkModelFitException.h" +#include "mitkModelFitResultRelationRule.h" void mitk::modelFit::ModelFitInfo::AddParameter(Parameter::Pointer p) { if (p.IsNull()) { mitkThrow() << "Given parameter must not be NULL"; } if (GetParameter(p->name, p->type).IsNull()) { MITK_DEBUG << "Adding parameter '" << p->name << "with type " << p->type << "' to modelFit '" << uid << "'."; LockType lock(mutex); parameterList.push_back(p); } else { MITK_DEBUG << "Parameter '" << p->name << "' of modelFit '" << uid << "' already exists. Aborting."; } } mitk::modelFit::Parameter::ConstPointer mitk::modelFit::ModelFitInfo::GetParameter(const std::string& name, const Parameter::Type& type) const { for (ConstIterType iter = parameterList.begin(); iter != parameterList.end(); ++iter) { Parameter::ConstPointer p = static_cast(*iter); if (p->name == name && p->type == type) { return p; } } return nullptr; } const mitk::modelFit::ModelFitInfo::ParamListType& mitk::modelFit::ModelFitInfo::GetParameters() const { return this->parameterList; }; void mitk::modelFit::ModelFitInfo::DeleteParameter(const std::string& name, const Parameter::Type& type) { for (IterType iter = parameterList.begin(); iter != parameterList.end(); ++iter) { Parameter::ConstPointer p = static_cast(*iter); if (p->name == name && p->type == type) { MITK_DEBUG << "Deleting parameter '" << name << " with type " << type << "' from modelFit '" << uid << "'."; LockType lock(mutex); parameterList.erase(iter); return; } } } const std::string mitk::modelFit::GetMandatoryProperty(const mitk::DataNode* node, const std::string& prop) { std::string result; if (!node || !node->GetData() || !node->GetData()->GetPropertyList()->GetStringProperty(prop.c_str(), result) || result.empty()) { mitkThrowException(mitk::modelFit::ModelFitException) << "Node " << node->GetName() << " is lacking the required " << "property '" << prop << "' or contains an empty string."; } return result; } const std::string mitk::modelFit::GetMandatoryProperty(const mitk::BaseData* data, const std::string& prop) { std::string result; if (!data || !data->GetPropertyList()->GetStringProperty(prop.c_str(), result) || result.empty()) { mitkThrowException(mitk::modelFit::ModelFitException) << "Data is lacking the required " << "property '" << prop << "' or contains an empty string."; } return result; } mitk::modelFit::ModelFitInfo::Pointer mitk::modelFit::CreateFitInfoFromNode(const ModelFitInfo::UIDType& uid, const mitk::DataStorage* storage) { if (!storage) { return nullptr; } mitk::DataStorage::SetOfObjects::ConstPointer nodes = GetNodesOfFit(uid, storage); if (nodes.IsNull() || nodes->empty()) { return nullptr; } mitk::DataNode::ConstPointer node = nodes->GetElement( 0).GetPointer(); //take one of the nodes as template if (!node->GetData()) { return nullptr; } ModelFitInfo::Pointer fit = ModelFitInfo::New(); fit->uid = uid; // Mandatory properties try { fit->fitType = GetMandatoryProperty(node, mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME()); fit->modelType = GetMandatoryProperty(node, mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME()); fit->modelName = GetMandatoryProperty(node, mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME()); } catch (const ModelFitException& e) { MITK_ERROR << e.what(); return nullptr; } // Either a function string or a function class must exist if (!node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), fit->function)) { fit->function = ""; } try { fit->functionClassID = GetMandatoryProperty(node, mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME()); } catch (const ModelFitException&) { if (fit->function.empty()) { MITK_ERROR << "The properties '" << mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME() << "'and '" << mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME() << "' are both empty or missing. One of these is required."; return nullptr; } } node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), fit->fitName); node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), fit->x); node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), fit->xAxisName); node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), fit->xAxisUnit); node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), fit->yAxisName); node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), fit->yAxisUnit); // Parameter for (DataStorage::SetOfObjects::ConstIterator pos = nodes->Begin(); pos != nodes->End(); ++pos) { modelFit::Parameter::Pointer param = ExtractParameterFromData(pos->Value()->GetData()); if (param.IsNotNull()) { fit->AddParameter(param); } } // Static parameters mitk::ScalarListLookupTableProperty::ConstPointer varProp = dynamic_cast(node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME().c_str()).GetPointer()); if (varProp.IsNotNull()) { const mitk::ScalarListLookupTable lut = varProp->GetValue(); const mitk::ScalarListLookupTable::LookupTableType& varMap = lut.GetLookupTable(); for (mitk::ScalarListLookupTable::LookupTableType::const_iterator mapIter = varMap.begin(); mapIter != varMap.end(); ++mapIter) { fit->staticParamMap.Add(mapIter->first, mapIter->second); } } //fit input and ROI - try - { - fit->inputUID = GetMandatoryProperty(node, - mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME()); - } - catch (const ModelFitException& e) - { - MITK_ERROR << e.what(); - return nullptr; - } - if (storage) { - mitk::DataNode::Pointer inputNode = GetNodeByModelFitUID(storage, fit->inputUID); - - if (inputNode.IsNull()) + auto inputRule = ModelFitResultRelationRule::New(); + auto inputPredicate = inputRule->GetDestinationsDetector(node); + auto inputNodes = storage->GetSubset(inputPredicate); + if (inputNodes->empty()) { - MITK_ERROR << "Cannot create valid model fit info. input node cannot be found."; + MITK_ERROR << "Cannot create valid model fit info. Input node cannot be found."; return nullptr; } + if (inputNodes->size()>1) + { + MITK_WARN << "More then one node found as input node. This could indicate an invalid state as only one input node should be present. Used first found input node."; + } - mitk::Image::Pointer inputImage = dynamic_cast(inputNode->GetData()); + auto inputNode = inputNodes->front(); + + mitk::Image::ConstPointer inputImage = dynamic_cast(inputNode->GetData()); if (inputImage.IsNull()) { MITK_ERROR << "Cannot create valid model fit info. input node does not contain an image."; return nullptr; } fit->inputImage = inputImage; } node->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME().c_str(), fit->roiUID); mitk::ScalarListLookupTableProperty::ConstPointer inputDataProp = dynamic_cast(node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME().c_str()).GetPointer()); if (inputDataProp.IsNotNull()) { fit->inputData = inputDataProp->GetValue(); } return fit; } mitk::modelFit::ModelFitInfo::Pointer mitk::modelFit::CreateFitInfoFromModelParameterizer(const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage, const std::string& fitType, const std::string& fitName, - const NodeUIDType roiUID) + const ModelFitInfo::UIDType& roiUID) { if (!usedParameterizer) { return nullptr; } UIDGenerator generator("FitUID_"); std::string uid = generator.GetUID(); ModelFitInfo::Pointer fit = ModelFitInfo::New(); fit->uid = uid; fit->fitType = fitType; fit->fitName = fitName; fit->inputImage = dynamic_cast(inputImage); - fit->inputUID = EnsureModelFitUID(inputImage); if (fit->inputImage.IsNull()) { mitkThrow() << "Cannot generate model fit info. Input node does not contain an image."; } fit->modelType = usedParameterizer->GetModelType(); fit->modelName = usedParameterizer->GetModelDisplayName(); fit->function = usedParameterizer->GetFunctionString(); fit->x = usedParameterizer->GetXName(); fit->functionClassID = usedParameterizer->GetClassID(); fit->xAxisName = usedParameterizer->GetXAxisName(); fit->xAxisUnit = usedParameterizer->GetXAxisUnit(); fit->yAxisName = usedParameterizer->GetYAxisName(); fit->yAxisUnit = usedParameterizer->GetYAxisUnit(); // Parameter ModelTraitsInterface::ParameterNamesType paramNames = usedParameterizer->GetParameterNames(); ModelTraitsInterface::ParamterScaleMapType paramScales = usedParameterizer->GetParameterScales(); ModelTraitsInterface::ParamterUnitMapType paramUnits = usedParameterizer->GetParameterUnits(); for (ModelTraitsInterface::ParameterNamesType::iterator pos = paramNames.begin(); pos != paramNames.end(); ++pos) { modelFit::Parameter::Pointer param = modelFit::Parameter::New(); param->name = *pos; param->type = Parameter::ParameterType; if (paramScales.find(*pos) == paramScales.end()) { mitkThrow() << "Cannot generate model fit info. Model traits invalid (scales do not include parameter). Parameter name: " << *pos; } if (paramUnits.find(*pos) == paramUnits.end()) { mitkThrow() << "Cannot generate model fit info. Model traits invalid (units do not include parameter). Parameter name: " << *pos; } param->scale = paramScales[*pos]; param->unit = paramUnits[*pos]; fit->AddParameter(param); } //derived parameter ModelTraitsInterface::DerivedParameterNamesType derivedNames = usedParameterizer->GetDerivedParameterNames(); ModelTraitsInterface::DerivedParamterScaleMapType derivedScales = usedParameterizer->GetDerivedParameterScales(); ModelTraitsInterface::DerivedParamterUnitMapType derivedUnits = usedParameterizer->GetDerivedParameterUnits(); for (ModelTraitsInterface::ParameterNamesType::iterator pos = derivedNames.begin(); pos != derivedNames.end(); ++pos) { modelFit::Parameter::Pointer param = modelFit::Parameter::New(); param->name = *pos; param->type = Parameter::DerivedType; if (derivedScales.find(*pos) == derivedScales.end()) { mitkThrow() << "Cannot generate model fit info. Model traits invalid (scales do not include parameter). Parameter name: " << *pos; } if (derivedUnits.find(*pos) == derivedUnits.end()) { mitkThrow() << "Cannot generate model fit info. Model traits invalid (units do not include parameter). Parameter name: " << *pos; } param->scale = derivedScales[*pos]; param->unit = derivedUnits[*pos]; fit->AddParameter(param); } // Static parameters (but transfer only the global ones) ModelParameterizerBase::StaticParameterMapType staticParamMap = usedParameterizer->GetGlobalStaticParameters(); for (ModelParameterizerBase::StaticParameterMapType::const_iterator pos = staticParamMap.begin(); pos != staticParamMap.end(); ++pos) { fit->staticParamMap.Add(pos->first, pos->second); } fit->roiUID = roiUID; return fit; } mitk::modelFit::ModelFitInfo::Pointer mitk::modelFit::CreateFitInfoFromModelParameterizer(const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage, const std::string& fitType, const ScalarListLookupTable& inputData, -const std::string& fitName, const NodeUIDType roiUID) +const std::string& fitName, const ModelFitInfo::UIDType& roiUID) { mitk::modelFit::ModelFitInfo::Pointer info = CreateFitInfoFromModelParameterizer(usedParameterizer, inputImage, fitType, fitName, roiUID); info->inputData = inputData; return info; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::modelFit::GetNodesOfFit(const ModelFitInfo::UIDType& fitUID, const mitk::DataStorage* storage) { if (!storage) { return nullptr; } mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(fitUID)); return storage->GetSubset(predicate); }; mitk::modelFit::NodeUIDSetType mitk::modelFit::GetFitUIDsOfNode(const mitk::DataNode* node, const mitk::DataStorage* storage) { + auto rule = ModelFitResultRelationRule::New(); mitk::modelFit::NodeUIDSetType result; if (node && storage) { - mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New( + auto fitUIDPredicate = mitk::NodePredicateDataProperty::New( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str()); - mitk::DataStorage::SetOfObjects::ConstPointer nodes = storage->GetDerivations(node, predicate, - false); + auto rulePredicate = rule->GetSourcesDetector(node); + auto predicate = NodePredicateAnd::New(fitUIDPredicate, rulePredicate); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = storage->GetSubset(predicate); for (mitk::DataStorage::SetOfObjects::ConstIterator pos = nodes->Begin(); pos != nodes->End(); ++pos) { mitk::modelFit::ModelFitInfo::UIDType uid; pos->Value()->GetData()->GetPropertyList()->GetStringProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), uid); + result.insert(uid); } } return result; }; diff --git a/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp b/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp index abeef72a37..a286aa8290 100644 --- a/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp +++ b/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp @@ -1,440 +1,438 @@ /*============================================================================ 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 "mitkModelFitPlotDataHelper.h" #include "mitkExceptionMacro.h" #include "mitkImage.h" #include "mitkModelFitParameterValueExtraction.h" #include "mitkModelGenerator.h" #include "mitkFormulaParser.h" const std::string mitk::MODEL_FIT_PLOT_SAMPLE_NAME() { return "Sample"; }; const std::string mitk::MODEL_FIT_PLOT_SIGNAL_NAME() { return "Signal"; } const std::string mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME() { return "INTERP_Signal"; }; void mitk::PlotDataCurve:: SetValues(const PlotDataValues& _arg) { if (this->m_Values != _arg) { this->m_Values = _arg; this->Modified(); } } void mitk::PlotDataCurve:: SetValues(PlotDataValues&& _arg) { if (this->m_Values != _arg) { this->m_Values = std::move(_arg); this->Modified(); } } mitk::PlotDataCurve & mitk::PlotDataCurve::operator=(const PlotDataCurve& rhs) { this->m_Values = rhs.m_Values; this->SetTimeStamp(rhs.GetTimeStamp()); return *this; }; mitk::PlotDataCurve& mitk::PlotDataCurve::operator=(PlotDataCurve&& rhs) noexcept { this->m_Values = std::move(rhs.m_Values); this->SetTimeStamp(rhs.GetTimeStamp()); return *this; }; void mitk::PlotDataCurve::Reset() { this->m_Values.clear(); this->Modified(); } mitk::PlotDataCurve::PlotDataCurve() { } const mitk::PlotDataCurve* GetPlotCurve(const mitk::PlotDataCurveCollection* collection, const std::string& key) { if (collection) { auto iter = collection->find(key); if (iter != collection->end()) { return iter->second.GetPointer(); } } return nullptr; }; const mitk::PlotDataCurve* mitk::ModelFitPlotData::GetSamplePlot(const PlotDataCurveCollection* coll) { if (coll) { return GetPlotCurve(coll, MODEL_FIT_PLOT_SAMPLE_NAME()); } return nullptr; }; const mitk::PlotDataCurve* mitk::ModelFitPlotData::GetSignalPlot(const PlotDataCurveCollection* coll) { if (coll) { return GetPlotCurve(coll, MODEL_FIT_PLOT_SIGNAL_NAME()); } return nullptr; }; const mitk::PlotDataCurve* mitk::ModelFitPlotData::GetInterpolatedSignalPlot(const PlotDataCurveCollection* coll) { if (coll) { return GetPlotCurve(coll, MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME()); } return nullptr; }; std::string mitk::ModelFitPlotData::GetPositionalCollectionName(const PositionalCollectionMap::value_type& mapValue) { std::ostringstream nameStrm; nameStrm.imbue(std::locale("C")); nameStrm << "Pos " << mapValue.first << std::endl << std::setprecision(3) << "(" << mapValue.second.first[0] << "|" << mapValue.second.first[1] << "|" << mapValue.second.first[2] << ")"; return nameStrm.str(); }; const mitk::PlotDataCurveCollection* mitk::ModelFitPlotData::GetPositionalPlot(const mitk::Point3D& point) const { auto predicate = [point](const PositionalCollectionMap::value_type& value) {return value.second.first == point; }; auto iter = std::find_if(std::begin(this->positionalPlots), std::end(this->positionalPlots), predicate); if (iter != positionalPlots.end()) { return iter->second.second.GetPointer(); } return nullptr; }; const mitk::PlotDataCurveCollection* mitk::ModelFitPlotData::GetPositionalPlot(mitk::PointSet::PointIdentifier id) const { auto iter = this->positionalPlots.find(id); if (iter != positionalPlots.end()) { return iter->second.second.GetPointer(); } return nullptr; }; mitk::PlotDataValues::value_type mitk::ModelFitPlotData::GetXMinMax() const { double max = itk::NumericTraits::NonpositiveMin(); double min = itk::NumericTraits::max(); //currently we assume that within a model fit, plot data does not exceed //the sample/signale on the x axis. auto sample = this->GetSamplePlot(this->currentPositionPlots); if (sample) { CheckXMinMaxFromPlotDataValues(sample->GetValues(), min, max); } for (const auto& posCollection : this->positionalPlots) { auto sample = this->GetSamplePlot(posCollection.second.second); if (sample) { CheckXMinMaxFromPlotDataValues(sample->GetValues(), min, max); } } return std::make_pair(min, max); }; mitk::PlotDataValues::value_type mitk::ModelFitPlotData::GetYMinMax() const { double max = itk::NumericTraits::NonpositiveMin(); double min = itk::NumericTraits::max(); for (const auto& plot : *(this->currentPositionPlots.GetPointer())) { CheckYMinMaxFromPlotDataValues(plot.second->GetValues(), min, max); } for (const auto& posCollection : this->positionalPlots) { for (const auto& plot : *(posCollection.second.second)) { CheckYMinMaxFromPlotDataValues(plot.second->GetValues(), min, max); } } for (const auto& plot : *(this->staticPlots)) { CheckYMinMaxFromPlotDataValues(plot.second->GetValues(), min, max); } return std::make_pair(min, max); }; mitk::ModelFitPlotData::ModelFitPlotData() { this->currentPositionPlots = PlotDataCurveCollection::New(); this->staticPlots = PlotDataCurveCollection::New(); }; void mitk::CheckYMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max) { for (const auto & pos : data) { if (max < pos.second) { max = pos.second; } if (min > pos.second) { min = pos.second; } } } void mitk::CheckXMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max) { for (const auto & pos : data) { if (max < pos.first) { max = pos.first; } if (min > pos.first) { min = pos.first; } } } /** Helper function that generates the curve based on a stored and on the fly parsed function string.*/ mitk::PlotDataCurve::Pointer CalcSignalFromFunction(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid) { if (!fitInfo) { mitkThrow() << "Cannot calc model curve from function for given fit. Passed ModelFitInfo instance is nullptr."; } - mitk::Image::Pointer inputImage = fitInfo->inputImage; - assert(inputImage.IsNotNull()); + assert(fitInfo->inputImage.IsNotNull()); mitk::PlotDataCurve::Pointer result = mitk::PlotDataCurve::New(); mitk::PlotDataCurve::ValuesType values; values.reserve(timeGrid.size()); // Calculate index ::itk::Index<3> index; - mitk::BaseGeometry::Pointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); + mitk::BaseGeometry::Pointer geometry = fitInfo->inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); geometry->WorldToIndex(position, index); mitk::ParameterValueMapType parameterMap = mitk::ExtractParameterValueMapFromModelFit(fitInfo, position); mitk::FormulaParser parser(¶meterMap); for (unsigned int t = 0; t < timeGrid.size(); ++t) { // Set up static parameters for (const auto& var : fitInfo->staticParamMap) { const auto& list = var.second; if (list.size() == 1) { parameterMap[var.first] = list.front(); } else { parameterMap[var.first] = list.at(t); } } // Calculate curve data double x = timeGrid[t]; parameterMap[fitInfo->x] = x; double y = parser.parse(fitInfo->function); values.emplace_back(std::make_pair(x, y)); } result->SetValues(std::move(values)); return result; } /** Helper function that generates the curve based on the model specified by the fit info.*/ mitk::PlotDataCurve::Pointer CalcSignalFromModel(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelParameterizerBase* parameterizer = nullptr) { assert(fitInfo); if (!parameterizer) { parameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*fitInfo); } - mitk::Image::Pointer inputImage = fitInfo->inputImage; - assert(inputImage.IsNotNull()); + assert(fitInfo->inputImage.IsNotNull()); // Calculate index ::itk::Index<3> index; - mitk::BaseGeometry::Pointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); + mitk::BaseGeometry::Pointer geometry = fitInfo->inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); geometry->WorldToIndex(position, index); //model generation mitk::ModelBase::Pointer model = parameterizer->GenerateParameterizedModel(index); mitk::ParameterValueMapType parameterMap = mitk::ExtractParameterValueMapFromModelFit(fitInfo, position); mitk::ModelBase::ParametersType paramArray = mitk::ConvertParameterMapToParameterVector(parameterMap, model); mitk::ModelBase::ModelResultType curveDataY = model->GetSignal(paramArray); mitk::PlotDataCurve::Pointer result = mitk::PlotDataCurve::New(); mitk::ModelBase::TimeGridType timeGrid = model->GetTimeGrid(); mitk::PlotDataCurve::ValuesType values; values.reserve(timeGrid.size()); for (unsigned int t = 0; t < timeGrid.size(); ++t) { double x = timeGrid[t]; double y = curveDataY[t]; values.emplace_back(std::make_pair(x, y)); } result->SetValues(std::move(values)); return result; } mitk::PlotDataCurve::Pointer mitk::GenerateModelSignalPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer) { if (!fitInfo) { mitkThrow() << "Cannot calc model curve from function for given fit. Passed ModelFitInfo instance is nullptr."; } mitk::PlotDataCurve::Pointer result; if (!parameterizer) { parameterizer = ModelGenerator::GenerateModelParameterizer(*fitInfo); } if (parameterizer) { // Use model instead of formula parser parameterizer->SetDefaultTimeGrid(timeGrid); result = CalcSignalFromModel(position, fitInfo, parameterizer); } else { // Use formula parser to parse function string try { result = CalcSignalFromFunction(position, fitInfo, timeGrid); } catch (const mitk::FormulaParserException& e) { MITK_ERROR << "Error while parsing modelfit function: " << e.what(); } } return result; } mitk::PlotDataCurveCollection::Pointer mitk::GenerateAdditionalModelFitPlotData(const mitk::Point3D& /*position*/, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid) { if (!fitInfo) { mitkThrow() << "Cannot calc model curve from function for given fit. Passed ModelFitInfo instance is nullptr."; } mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New(); for (const auto& additionalInput : fitInfo->inputData.GetLookupTable()) { if (additionalInput.second.size() != timeGrid.size()) { MITK_ERROR << "Error while refreshing input data for visualization. Size of data and input image time grid differ. Invalid data name: " << additionalInput.first; } else { mitk::PlotDataCurve::Pointer pointData = mitk::PlotDataCurve::New();; mitk::PlotDataCurve::ValuesType values; values.reserve(timeGrid.size()); for (unsigned int t = 0; t < timeGrid.size(); ++t) { const double x = timeGrid[t]; const double y = additionalInput.second[t]; values.emplace_back(std::make_pair(x, y)); } pointData->SetValues(std::move(values)); result->CastToSTLContainer().emplace(additionalInput.first, std::move(pointData)); } } return result; } mitk::PlotDataCurve::Pointer mitk::GenerateImageSamplePlotData(const mitk::Point3D& position, const mitk::Image* image, const mitk::ModelBase::TimeGridType& timeGrid) { if (!image) { mitkThrow() << "Cannot generate sample plot data. Passed image instance is nullptr."; } mitk::PlotDataCurve::Pointer result = mitk::PlotDataCurve::New(); mitk::PlotDataCurve::ValuesType values; values.reserve(timeGrid.size()); for (unsigned int t = 0; t < timeGrid.size(); ++t) { const double x = timeGrid[t]; const double y = ReadVoxel(image, position, t); values.emplace_back(std::make_pair(x, y)); } result->SetValues(std::move(values)); return result; } diff --git a/Modules/ModelFit/src/Common/mitkModelFitResultHelper.cpp b/Modules/ModelFit/src/Common/mitkModelFitResultHelper.cpp index 5a18753ad0..72be8d9915 100644 --- a/Modules/ModelFit/src/Common/mitkModelFitResultHelper.cpp +++ b/Modules/ModelFit/src/Common/mitkModelFitResultHelper.cpp @@ -1,283 +1,280 @@ /*============================================================================ 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 "mitkModelFitResultHelper.h" #include #include #include "mitkModelTraitsInterface.h" #include "mitkModelFitConstants.h" #include "mitkModelFitInfo.h" +#include #include #include namespace mitk { namespace modelFit { void AdaptDataPropertyToParameter(mitk::BaseData* data, const ModelBase::ParameterNameType& name, modelFit::Parameter::Type dataType, const modelFit::ModelFitInfo* fitInfo) { assert(data); if (!data) { mitkThrow() << "Cannot add model or fit properties to data instance. Passed data instance is null. parameter name:" << name; } if (!fitInfo) { mitkThrow() << "Cannot add model or fit properties to data. Passed fit info instance is null. parameter name:" << name; } data->GetPropertyList()->SetStringProperty(ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(),name.c_str()); if (dataType == modelFit::Parameter::ParameterType) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str()); } else if (dataType == modelFit::Parameter::DerivedType) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str()); } else if (dataType == modelFit::Parameter::CriterionType) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), ModelFitConstants::PARAMETER_TYPE_VALUE_CRITERION().c_str()); } else if (dataType == modelFit::Parameter::EvaluationType) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), ModelFitConstants::PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER().c_str()); } if (dataType == modelFit::Parameter::ParameterType || dataType == modelFit::Parameter::DerivedType) { modelFit::Parameter::ConstPointer param = fitInfo->GetParameter(name,dataType); if (param.IsNull()) { mitkThrow() << "Cannot generate model fit result data. Parameter name is not part of the model fit info. Parameter name: "<unit.empty()) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), param->unit.c_str()); } if (param->scale != 1.0) { data->GetPropertyList()->SetFloatProperty(ModelFitConstants::PARAMETER_SCALE_PROPERTY_NAME().c_str(), param->scale); } } - }; + } void AdaptDataPropertyToModelFit(mitk::BaseData* data, const modelFit::ModelFitInfo* fitInfo) { assert(data); if (!data) { mitkThrow() << "Cannot add model or fit properties to data. Passed data instance is null."; } if (!fitInfo) { mitkThrow() << "Cannot add model or fit properties to data. Passed model traits instance is null."; } //model section data->GetPropertyList()->SetStringProperty(ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), fitInfo->modelType.c_str()); data->GetPropertyList()->SetStringProperty(ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), fitInfo->modelName.c_str()); data->GetPropertyList()->SetStringProperty(ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), fitInfo->functionClassID.c_str()); if(!(fitInfo->function.empty())) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), fitInfo->function.c_str()); data->GetPropertyList()->SetStringProperty(ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), fitInfo->x.c_str()); } //axis section if (!fitInfo->xAxisName.empty()) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), fitInfo->xAxisName.c_str()); } if (!fitInfo->xAxisUnit.empty()) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), fitInfo->xAxisUnit.c_str()); } if (!fitInfo->yAxisName.empty()) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), fitInfo->yAxisName.c_str()); } if (!fitInfo->yAxisUnit.empty()) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), fitInfo->yAxisUnit.c_str()); } //fit section data->GetPropertyList()->SetStringProperty(ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), fitInfo->uid.c_str()); data->GetPropertyList()->SetStringProperty(ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), fitInfo->fitName.c_str()); data->GetPropertyList()->SetStringProperty(ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), fitInfo->fitType.c_str()); - data->GetPropertyList()->SetStringProperty(ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), fitInfo->inputUID.c_str()); + + const auto rule = ModelFitResultRelationRule::New(); + rule->Connect(dynamic_cast(data), fitInfo->inputImage); if (fitInfo->inputData.GetLookupTable().size() > 0) { mitk::ScalarListLookupTableProperty::Pointer inputDataProp = mitk::ScalarListLookupTableProperty::New(); inputDataProp->SetValue(fitInfo->inputData); data->SetProperty(ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME().c_str(), inputDataProp); } if (!fitInfo->roiUID.empty()) { data->GetPropertyList()->SetStringProperty(ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME().c_str(), fitInfo->roiUID.c_str()); } data->SetProperty(ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME().c_str(), ConvertStaticParametersToProperty(fitInfo->staticParamMap)); - }; + } mitk::DataNode::Pointer CreateNode(const ModelBase::ParameterNameType& name, Image* parameterImage, const ModelFitInfo* fitInfo) { if (!parameterImage) { mitkThrow() << "Cannot generate model fit result node. Passed parameterImage is null. parameter name: "<SetData(parameterImage); std::string nodeName = name; if (!fitInfo->fitName.empty()) { nodeName = fitInfo->fitName + "_" + nodeName; } result->SetName(nodeName); result->SetVisibility(false); return result; - }; + } } } mitk::ScalarListLookupTableProperty::Pointer mitk::modelFit::ConvertStaticParametersToProperty(const mitk::modelFit::StaticParameterMap& params) { mitk::ScalarListLookupTableProperty::Pointer result = mitk::ScalarListLookupTableProperty::New(); ScalarListLookupTable table; for(mitk::modelFit::StaticParameterMap::const_iterator pos = params.begin(); pos != params.end(); ++pos) { table.SetTableValue(pos->first,pos->second); } result->SetValue(table); return result; -}; +} MITKMODELFIT_EXPORT void mitk::modelFit::SetModelFitDataProperties(mitk::BaseData* data, const ModelBase::ParameterNameType& name, modelFit::Parameter::Type dataType, const modelFit::ModelFitInfo* fitInfo) { AdaptDataPropertyToModelFit(data, fitInfo); AdaptDataPropertyToParameter(data, name, dataType, fitInfo); -}; +} MITKMODELFIT_EXPORT mitk::DataNode::Pointer mitk::modelFit::CreateResultNode( const ModelBase::ParameterNameType& name, modelFit::Parameter::Type nodeType, Image* parameterImage, const ModelFitInfo* modelFitInfo) { if (!parameterImage) { mitkThrow() << "Cannot generate model fit result node. Passed parameterImage is null. parameter name: "<inputImage, parameterImage); + mitk::DICOMPMPropertyHelper::DeriveDICOMPMProperties(parameterImage); + return result; -}; +} MITKMODELFIT_EXPORT mitk::modelFit::ModelFitResultNodeVectorType mitk::modelFit::CreateResultNodeMap( const ModelFitResultImageMapType& results, const ModelFitResultImageMapType& derivedResults, const ModelFitResultImageMapType& criterionResults, const ModelFitResultImageMapType& evaluationResults, const ModelFitInfo* fitInfo) { if (!fitInfo) { mitkThrow() << "Cannot store model fit results in data storage. Passed model traits instance is null."; } ModelFitResultNodeVectorType nodes; for (ModelFitResultImageMapType::const_iterator pos = results.begin(); pos!=results.end(); ++pos) { DataNode::Pointer newNode = CreateResultNode(pos->first, modelFit::Parameter::ParameterType, pos->second, fitInfo); nodes.push_back(newNode); } for (ModelFitResultImageMapType::const_iterator pos = derivedResults.begin(); pos!=derivedResults.end(); ++pos) { DataNode::Pointer newNode = CreateResultNode(pos->first, modelFit::Parameter::DerivedType, pos->second, fitInfo); nodes.push_back(newNode); } for (ModelFitResultImageMapType::const_iterator pos = criterionResults.begin(); pos!=criterionResults.end(); ++pos) { DataNode::Pointer newNode = CreateResultNode(pos->first, modelFit::Parameter::CriterionType, pos->second, fitInfo); nodes.push_back(newNode); } for (ModelFitResultImageMapType::const_iterator pos = evaluationResults.begin(); pos!=evaluationResults.end(); ++pos) { DataNode::Pointer newNode = CreateResultNode(pos->first, modelFit::Parameter::EvaluationType, pos->second, fitInfo); nodes.push_back(newNode); } return nodes; -}; +} MITKMODELFIT_EXPORT void mitk::modelFit::StoreResultsInDataStorage(DataStorage* storage, const ModelFitResultNodeVectorType& resultNodes, DataNode* parentNode) { if (!storage) { mitkThrow() << "Cannot store model fit results in data storage. Passed storage is null."; } for (ModelFitResultNodeVectorType::const_iterator pos = resultNodes.begin(); pos!=resultNodes.end(); ++pos) { storage->Add(*pos,parentNode); } - - // Set DICOM properties, paramap-secific (DICOMPM) and general properties from source data (DICOMQI) - - for (ModelFitResultNodeVectorType::const_iterator pos = resultNodes.begin(); pos != resultNodes.end(); pos++) - { - mitk::DICOMQIPropertyHelper::DeriveDICOMSourceProperties(parentNode->GetData(), pos->GetPointer()->GetData()); - mitk::DICOMPMPropertyHelper::DeriveDICOMPMProperties(pos->GetPointer()->GetData()); - } - - -}; +} diff --git a/Modules/ModelFit/src/Common/mitkModelFitResultRelationRule.cpp b/Modules/ModelFit/src/Common/mitkModelFitResultRelationRule.cpp new file mode 100644 index 0000000000..5f20ad5b05 --- /dev/null +++ b/Modules/ModelFit/src/Common/mitkModelFitResultRelationRule.cpp @@ -0,0 +1,80 @@ +/*============================================================================ + +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 "mitkModelFitResultRelationRule.h" +#include "mitkModelFitConstants.h" + +#include + +bool mitk::ModelFitResultRelationRule::IsDestinationCandidate(const IPropertyProvider *owner) const +{ + auto node = dynamic_cast(owner); + + auto image = nullptr != node + ? dynamic_cast(node->GetData()) + : dynamic_cast(owner); + + return image != nullptr && image->GetTimeSteps()>1; +} + + +mitk::ModelFitResultRelationRule::ModelFitResultRelationRule() + : SourceImageRelationRule("Model fit input", "Model fit result relation", "fit result", "source image") +{} + + +mitk::ModelFitResultRelationRule::DataRelationUIDVectorType +mitk::ModelFitResultRelationRule::GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const +{ + DataRelationUIDVectorType result = Superclass::GetRelationUIDs_DataLayer(source, destination, instances_IDLayer); + + if (result.empty()) + { //check if there is a relation based on legacy ModelFitUID + auto imageUIDprop = source->GetConstProperty(ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME()); + if (imageUIDprop.IsNotNull()) + { + const auto inputImageUID = imageUIDprop->GetValueAsString(); + bool isValid = nullptr == destination; + if (nullptr != destination) + { + auto modelFitUIDprop = destination->GetConstProperty(ModelFitConstants::LEGACY_UID_PROPERTY_NAME()); + + if (modelFitUIDprop.IsNotNull()) + { + const auto destinationUID = modelFitUIDprop->GetValueAsString(); + isValid = destinationUID == inputImageUID; + } + } + if (isValid) + { + result.emplace_back("model.fit.input.image.legacy.relation", this->GetRuleID()); + } + } + } + + return result; +} + +void mitk::ModelFitResultRelationRule::Disconnect_datalayer(IPropertyOwner * source, const RelationUIDType & relationUID) const +{ + Superclass::Disconnect_datalayer(source, relationUID); + //Legacy connection (LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME) cannot be + //disconnected on purpose. +} + +itk::LightObject::Pointer mitk::ModelFitResultRelationRule::InternalClone() const +{ + itk::LightObject::Pointer result = Self::New().GetPointer(); + + return result; +} diff --git a/Modules/ModelFit/src/Common/mitkModelFitUIDHelper.cpp b/Modules/ModelFit/src/Common/mitkModelFitUIDHelper.cpp deleted file mode 100644 index 1e9bda1e0e..0000000000 --- a/Modules/ModelFit/src/Common/mitkModelFitUIDHelper.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/*============================================================================ - -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 "mitkModelFitUIDHelper.h" - - -// Mitk -#include -#include -#include -#include -#include -#include -#include - -mitk::DataNode::Pointer mitk::GetNodeByModelFitUID(const mitk::DataStorage* storage, const NodeUIDType& uid) -{ - mitk::DataNode::Pointer result; - - if (storage) - { - auto nodes = storage->GetAll(); - for (auto node : *(nodes.GetPointer())) - { - if (CheckModelFitUID(node, uid)) - { - result = node; - break; - } - } - } - - return result; -} - -mitk::NodeUIDType mitk::EnsureModelFitUID(mitk::DataNode* node) -{ - if (!node) - { - mitkThrow() << "Cannot ensure node UID. Passed node pointer is NULL."; - } - - return EnsureModelFitUID(node->GetData()); -}; - -mitk::NodeUIDType mitk::EnsureModelFitUID(mitk::BaseData* data) -{ - if (!data) - { - mitkThrow() << "Cannot ensure node UID. Passed node pointer is NULL."; - } - - BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::UID_PROPERTY_NAME().c_str()); - std::string propUID = ""; - - - if (uidProp.IsNotNull()) - { - propUID = uidProp->GetValueAsString(); - } - else - { - //if the "user" defined UID is not found we will check if there is a DICOM series instance UID and use this value if available. - uidProp = data->GetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str()); - - if (uidProp.IsNotNull()) - { - propUID = uidProp->GetValueAsString(); - } - else - { - mitk::UIDGenerator generator; - propUID = generator.GetUID(); - - data->SetProperty(mitk::ModelFitConstants::UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(propUID)); - } - } - - return propUID; -}; - -bool mitk::CheckModelFitUID(const mitk::DataNode* node, const NodeUIDType& uid) -{ - if (node) - { - return CheckModelFitUID(node->GetData(), uid); - } - - return false; -}; - -bool mitk::CheckModelFitUID(const mitk::BaseData* data, const NodeUIDType& uid) -{ - bool result = false; - - if (data) - { - BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::UID_PROPERTY_NAME().c_str()); - - if (uidProp.IsNotNull()) - { - result = uidProp->GetValueAsString() == uid; - } - - if (!result) - { - //if the "user" defined UID does not match, we will check if there is a DICOM series instance UID and use this value for the check. - uidProp = data->GetProperty(GeneratePropertyNameForDICOMTag(0x0020, 0x000e).c_str()); - - if (uidProp.IsNotNull()) - { - result = uidProp->GetValueAsString() == uid; - } - } - } - - return result; -}; - diff --git a/Modules/ModelFit/test/files.cmake b/Modules/ModelFit/test/files.cmake index 8e408bd49c..7a2d71e252 100644 --- a/Modules/ModelFit/test/files.cmake +++ b/Modules/ModelFit/test/files.cmake @@ -1,16 +1,16 @@ SET(MODULE_TESTS itkMultiOutputNaryFunctorImageFilterTest.cpp itkMaskedStatisticsImageFilterTest.cpp itkMaskedNaryStatisticsImageFilterTest.cpp mitkLevenbergMarquardtModelFitFunctorTest.cpp mitkPixelBasedParameterFitImageGeneratorTest.cpp mitkROIBasedParameterFitImageGeneratorTest.cpp mitkMaskedDynamicImageStatisticsGeneratorTest.cpp mitkModelFitInfoTest.cpp - mitkModelFitUIDHelperTest.cpp mitkModelFitStaticParameterMapTest.cpp mitkSimpleBarrierConstraintCheckerTest.cpp mitkMVConstrainedCostFunctionDecoratorTest.cpp mitkConcreteModelFactoryBaseTest.cpp mitkFormulaParserTest.cpp + mitkModelFitResultRelationRuleTest.cpp ) diff --git a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp index 8ec84dca41..4793618497 100644 --- a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp +++ b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp @@ -1,267 +1,424 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include -#include "mitkTestingMacros.h" + #include "mitkProperties.h" #include "mitkStandaloneDataStorage.h" #include "mitkModelFitInfo.h" #include "mitkModelFitConstants.h" #include "mitkModelFitException.h" +#include "mitkModelFitResultRelationRule.h" +#include +#include + + +mitk::modelFit::ModelFitInfo::UIDType ensureModelFitUID(mitk::BaseData * data) +{ + mitk::BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str()); + std::string propUID = ""; + + if (uidProp.IsNotNull()) + { + propUID = uidProp->GetValueAsString(); + } + else + { + mitk::UIDGenerator generator; + propUID = generator.GetUID(); + + data->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(propUID)); + } + return propUID; +}; mitk::DataNode::Pointer generateModelFitTestNode() { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetName("Param1"); auto testImage = mitk::Image::New(); node->SetData(testImage); testImage->SetProperty("modelfit.testEmpty", mitk::StringProperty::New("")); testImage->SetProperty("modelfit.testValid", mitk::StringProperty::New("test")); - mitk::EnsureModelFitUID(node); + ensureModelFitUID(testImage); - testImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit1")); + testImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); testImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName")); testImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); - testImage->SetProperty(mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("input UID")); + testImage->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("input UID")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); testImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); testImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); testImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); testImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); return node; } -mitk::StandaloneDataStorage::Pointer generateModelFitTestStorage() -{ - mitk::StandaloneDataStorage::Pointer storage = mitk::StandaloneDataStorage::New(); - - //create input node - mitk::DataNode::Pointer inputNode = mitk::DataNode::New(); - inputNode->SetName("Input"); - mitk::Image::Pointer image = mitk::Image::New(); - inputNode->SetData(image); - mitk::NodeUIDType inputUID = mitk::EnsureModelFitUID(inputNode); - - storage->Add(inputNode); - - mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); - parents->push_back(inputNode); - - //create first result for Fit1 - mitk::DataNode::Pointer node1 = mitk::DataNode::New(); - mitk::Image::Pointer paramImage = mitk::Image::New(); - node1->SetData(paramImage); - node1->SetName("Param1"); - mitk::EnsureModelFitUID(node1); - paramImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit1")); - paramImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); - paramImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); - paramImage->SetProperty(mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); - - paramImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); - paramImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); - paramImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); - paramImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); - paramImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); - - paramImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); - paramImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); - paramImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); - paramImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); - - paramImage->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1")); - paramImage->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); - paramImage->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); - - storage->Add(node1,parents); - - //create second result for Fit1 - mitk::DataNode::Pointer node2 = mitk::DataNode::New(); - node2->SetName("Param2"); - mitk::Image::Pointer paramImage2 = mitk::Image::New(); - node2->SetData(paramImage2); - mitk::EnsureModelFitUID(node2); - paramImage2->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit1")); - paramImage2->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); - paramImage2->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); - paramImage2->SetProperty(mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); - - paramImage2->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); - paramImage2->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); - paramImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); - paramImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); - paramImage2->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); - - paramImage2->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); - paramImage2->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); - paramImage2->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); - paramImage2->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); - - paramImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2")); - paramImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); - paramImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); - - storage->Add(node2, parents); - - //create result for Fit 2 - mitk::DataNode::Pointer node3 = mitk::DataNode::New(); - node3->SetName("Param_Other"); - mitk::Image::Pointer paramImage3 = mitk::Image::New(); - node3->SetData(paramImage3); - mitk::EnsureModelFitUID(node3); - paramImage3->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit2")); - paramImage3->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName2")); - paramImage3->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); - paramImage3->SetProperty(mitk::ModelFitConstants::FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); - - paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); - paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_2")); - paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); - paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass_B")); - - paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param_Other")); - paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); - - storage->Add(node3, parents); - - return storage; -} - -int mitkModelFitInfoTest(int /*argc*/, char*[] /*argv[]*/) +class mitkModelFitInfoTestSuite : public mitk::TestFixture { - MITK_TEST_BEGIN("mitkModelFitTest") - - mitk::modelFit::Parameter::Pointer p = mitk::modelFit::Parameter::New(); - p->name = "p"; - mitk::modelFit::ModelFitInfo::Pointer fit = mitk::modelFit::ModelFitInfo::New(); - fit->AddParameter(p); - MITK_TEST_CONDITION_REQUIRED(fit->GetParameters().size() == 1, - "Testing if AddParameter successfully adds a parameter."); - - mitk::modelFit::Parameter::ConstPointer resultParam = fit->GetParameter("test", - mitk::modelFit::Parameter::ParameterType); - MITK_TEST_CONDITION_REQUIRED(resultParam.IsNull(), - "Testing if GetParameter returns NULL for wrong parameter."); - - resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::ParameterType); - MITK_TEST_CONDITION_REQUIRED(resultParam == p, - "Testing if GetParameter returns the correct parameter."); - - p->type = mitk::modelFit::Parameter::CriterionType; - resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::CriterionType); - MITK_TEST_CONDITION_REQUIRED(resultParam == p, - "Testing if GetParameter returns the correct parameter with a " << - "non-default type."); - - fit->DeleteParameter("test", mitk::modelFit::Parameter::CriterionType); - MITK_TEST_CONDITION_REQUIRED(fit->GetParameters().size() == 1, - "Testing if DeleteParameter fails for wrong parameter."); - - fit->DeleteParameter("p", mitk::modelFit::Parameter::CriterionType); - MITK_TEST_CONDITION_REQUIRED(fit->GetParameters().size() == 0, - "Testing if DeleteParameter successfully removes a parameter."); - - mitk::DataNode::Pointer node = generateModelFitTestNode(); - mitk::DataNode::Pointer invalideNode = mitk::DataNode::New(); - - MITK_TEST_FOR_EXCEPTION(mitk::modelFit::ModelFitException, - mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testInvalid")); - MITK_TEST_FOR_EXCEPTION(mitk::modelFit::ModelFitException, - mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testEmpty")); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testValid") - == "test", - "Testing if GetMandatoryProperty returns the correct value."); - - mitk::StandaloneDataStorage::Pointer storage = generateModelFitTestStorage(); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::CreateFitInfoFromNode("Fit1",nullptr).IsNull(), - "Testing if CreateFitInfoFromNode returns NULL for invalid node."); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::CreateFitInfoFromNode("invalide_UID",storage).IsNull(), - "Testing if CreateFitInfoFromNode returns NULL for node with " << - "missing properties."); - - mitk::DataNode::Pointer testNode = storage->GetNamedNode("Param1"); - mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("Fit1", storage); - MITK_TEST_CONDITION_REQUIRED(resultFit.IsNotNull(), - "Testing if CreateFitInfoFromNode returns a valid model fit info."); - MITK_TEST_CONDITION_REQUIRED(resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && - resultFit->uid == "Fit1" && - resultFit->fitName == "MyName1" && - resultFit->modelType == "TestModels" && - resultFit->modelName == "TestModel_1" && - resultFit->function == ""&& - resultFit->functionClassID == "ModelClass" && - resultFit->x == "myX" && - resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && - resultFit->xAxisUnit == "h" && - resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && - resultFit->yAxisUnit == "kg" && - resultFit->GetParameters().size() == 2, - "Testing if CreateFitInfoFromNode creates a fit with correct attributes."); - - mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1",mitk::modelFit::Parameter::ParameterType); - MITK_TEST_CONDITION_REQUIRED(param.IsNotNull(), - "Testing if param 1 exists."); - - MITK_TEST_CONDITION_REQUIRED(param->name == "Param1" && param->unit == "b" && param->image == testNode->GetData(), - "Testing if param 1 is configured correctly."); - - mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2",mitk::modelFit::Parameter::DerivedType); - MITK_TEST_CONDITION_REQUIRED(param2.IsNotNull(), - "Testing if param 2 exists."); - - testNode = storage->GetNamedNode("Param2"); - MITK_TEST_CONDITION_REQUIRED(param2->name == "Param2" && param2->unit == "a" && param2->image == testNode->GetData(), - "Testing if param 2 is configured correctly."); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::GetNodesOfFit(resultFit->uid,storage)->Size() == 2, - "Testing if GetNodesOfFit works correctly for Fit1"); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::GetNodesOfFit("Fit2",storage)->Size() == 1, - "Testing if GetNodesOfFit works correctly for Fit1"); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::GetNodesOfFit("unkown_fit",storage)->Size() == 0, - "Testing if GetNodesOfFit works correctly for unkown fits."); - - MITK_TEST_CONDITION_REQUIRED(mitk::modelFit::GetNodesOfFit("unkown_fit",nullptr).IsNull(), - "Testing if GetNodesOfFit works correctly for illegal calls."); - - testNode = storage->GetNamedNode("Input"); - mitk::modelFit::NodeUIDSetType uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode,storage); - - MITK_TEST_CONDITION_REQUIRED(uidSet.size() == 2 && - uidSet.find("Fit1")!=uidSet.end() && - uidSet.find("Fit2")!=uidSet.end(), - "Testing if GetFitUIDsOfNode works correctly."); - - uidSet = mitk::modelFit::GetFitUIDsOfNode(nullptr,storage); - - MITK_TEST_CONDITION_REQUIRED(uidSet.size() == 0, - "Testing if GetFitUIDsOfNode works correctly with invalid node."); + CPPUNIT_TEST_SUITE(mitkModelFitInfoTestSuite); + MITK_TEST(CheckModelFitInfo); + MITK_TEST(CheckGetMandatoryProperty); + MITK_TEST(CheckCreateFitInfoFromNode_Legacy); + MITK_TEST(CheckCreateFitInfoFromNode); + MITK_TEST(CheckGetNodesOfFit); + MITK_TEST(CheckGetFitUIDsOfNode); + + CPPUNIT_TEST_SUITE_END(); + + mitk::StandaloneDataStorage::Pointer m_Storage; + mitk::Image::Pointer m_ParamImage; + mitk::Image::Pointer m_ParamImage2; + mitk::Image::Pointer m_ParamImage_legacy; + mitk::Image::Pointer m_ParamImage2_legacy; + mitk::DataNode::Pointer m_ParamImageNode; + mitk::DataNode::Pointer m_ParamImageNode2; + mitk::DataNode::Pointer m_ParamImageNode_legacy; + mitk::DataNode::Pointer m_ParamImageNode2_legacy; + +public: + void setUp() override + { + m_Storage = mitk::StandaloneDataStorage::New(); + //create input node + mitk::DataNode::Pointer inputNode = mitk::DataNode::New(); + inputNode->SetName("Input"); + mitk::Image::Pointer image = mitk::Image::New(); + inputNode->SetData(image); + mitk::modelFit::ModelFitInfo::UIDType inputUID = ensureModelFitUID(image); + + m_Storage->Add(inputNode); + + mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); + parents->push_back(inputNode); + + ///////////////////////////////////////////////////// + //Create nodes for a fit (new style using rules) + ///////////////////////////////////////////////////// + auto rule = mitk::ModelFitResultRelationRule::New(); + //create first result for FitLegacy + m_ParamImageNode = mitk::DataNode::New(); + m_ParamImage = mitk::Image::New(); + m_ParamImageNode->SetData(m_ParamImage); + m_ParamImageNode->SetName("Param1"); + m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); + + m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); + + m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); + + m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); + m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); + rule->Connect(m_ParamImage, image); + m_Storage->Add(m_ParamImageNode, parents); + + //create second result for Fit + m_ParamImageNode2 = mitk::DataNode::New(); + m_ParamImageNode2->SetName("Param2"); + m_ParamImage2 = mitk::Image::New(); + m_ParamImageNode2->SetData(m_ParamImage2); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); + + m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); + + m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); + + m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); + m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); + rule->Connect(m_ParamImage2, image); + m_Storage->Add(m_ParamImageNode2, parents); + + ///////////////////////////////////////////////////// + //Create nodes for a fit in legacy mode (old fit uid) + ///////////////////////////////////////////////////// + + //create first result for FitLegacy + m_ParamImageNode_legacy = mitk::DataNode::New(); + m_ParamImage_legacy = mitk::Image::New(); + m_ParamImageNode_legacy->SetData(m_ParamImage_legacy); + m_ParamImageNode_legacy->SetName("Param1_legacy"); + ensureModelFitUID(m_ParamImage_legacy); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); + + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); + + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); + + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1_legacy")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); + m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); + + m_Storage->Add(m_ParamImageNode_legacy, parents); + + //create second result for FitLegacy + m_ParamImageNode2_legacy = mitk::DataNode::New(); + m_ParamImageNode2_legacy->SetName("Param2_legacy"); + m_ParamImage2_legacy = mitk::Image::New(); + m_ParamImageNode2_legacy->SetData(m_ParamImage2_legacy); + ensureModelFitUID(m_ParamImage2_legacy); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); + + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); + + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); + + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2_legacy")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); + m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); + + m_Storage->Add(m_ParamImageNode2_legacy, parents); + + ///////////////////////////////////////////////////// + //Create nodes for a fit on other input + auto anotherInputNode = mitk::DataNode::New(); + anotherInputNode->SetName("AnotherInput"); + auto anotherImage = mitk::Image::New(); + anotherInputNode->SetData(anotherImage); + + m_Storage->Add(anotherInputNode); + + parents = mitk::DataStorage::SetOfObjects::New(); + parents->push_back(anotherInputNode); + + mitk::DataNode::Pointer node3 = mitk::DataNode::New(); + node3->SetName("Param_Other"); + mitk::Image::Pointer paramImage3 = mitk::Image::New(); + node3->SetData(paramImage3); + paramImage3->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit2")); + paramImage3->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName2")); + paramImage3->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); + + paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); + paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_2")); + paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); + paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass_B")); + + paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param_Other")); + paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); + rule->Connect(paramImage3, anotherImage); + m_Storage->Add(node3, parents); + + } + + void tearDown() override + { + } + + void CheckModelFitInfo() + { + mitk::modelFit::Parameter::Pointer p = mitk::modelFit::Parameter::New(); + p->name = "p"; + mitk::modelFit::ModelFitInfo::Pointer fit = mitk::modelFit::ModelFitInfo::New(); + fit->AddParameter(p); + CPPUNIT_ASSERT_MESSAGE("AddParameter unsuccessfully adds a parameter.", fit->GetParameters().size() == 1); + + mitk::modelFit::Parameter::ConstPointer resultParam = fit->GetParameter("test", + mitk::modelFit::Parameter::ParameterType); + CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns NULL for wrong parameter.", resultParam.IsNull()); + + resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::ParameterType); + CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter.", resultParam == p); + + p->type = mitk::modelFit::Parameter::CriterionType; + resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::CriterionType); + CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter with a non-default type.", resultParam == p); + + fit->DeleteParameter("test", mitk::modelFit::Parameter::CriterionType); + CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter fails for wrong parameter.", fit->GetParameters().size() == 1); + + fit->DeleteParameter("p", mitk::modelFit::Parameter::CriterionType); + CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter successfully removes a parameter.", fit->GetParameters().size() == 0); + } + + void CheckGetMandatoryProperty() + { + mitk::DataNode::Pointer node = generateModelFitTestNode(); + mitk::DataNode::Pointer invalideNode = mitk::DataNode::New(); + + CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testInvalid"), mitk::modelFit::ModelFitException); + CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testEmpty"), mitk::modelFit::ModelFitException); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetMandatoryProperty returns the correct value.", mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testValid") + == "test"); + } + + void CheckCreateFitInfoFromNode_Legacy() + { + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("FitLegacy", nullptr).IsNull()); + + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull()); + + mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("FitLegacy", m_Storage); + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.", + resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && + resultFit->uid == "FitLegacy" && + resultFit->fitName == "MyName1" && + resultFit->modelType == "TestModels" && + resultFit->modelName == "TestModel_1" && + resultFit->function == "" && + resultFit->functionClassID == "ModelClass" && + resultFit->x == "myX" && + resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && + resultFit->xAxisUnit == "h" && + resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && + resultFit->yAxisUnit == "kg" && + resultFit->GetParameters().size() == 2); + + mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1_legacy", mitk::modelFit::Parameter::ParameterType); + CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull()); + + CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1_legacy" && param->unit == "b" && param->image == m_ParamImage_legacy); + + mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2_legacy", mitk::modelFit::Parameter::DerivedType); + CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull()); + + CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2_legacy" && param2->unit == "a" && param2->image == m_ParamImage2_legacy); + } + + void CheckCreateFitInfoFromNode() + { + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("Fit", nullptr).IsNull()); + + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull()); + + mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("Fit", m_Storage); + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull()); + CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.", + resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && + resultFit->uid == "Fit" && + resultFit->fitName == "MyName1" && + resultFit->modelType == "TestModels" && + resultFit->modelName == "TestModel_1" && + resultFit->function == "" && + resultFit->functionClassID == "ModelClass" && + resultFit->x == "myX" && + resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && + resultFit->xAxisUnit == "h" && + resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && + resultFit->yAxisUnit == "kg" && + resultFit->GetParameters().size() == 2); + + mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1", mitk::modelFit::Parameter::ParameterType); + CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull()); + + CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1" && param->unit == "b" && param->image == m_ParamImage); + + mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2", mitk::modelFit::Parameter::DerivedType); + CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull()); + + CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2" && param2->unit == "a" && param2->image == m_ParamImage2); + } + + void CheckGetNodesOfFit() + { + auto nodes = mitk::modelFit::GetNodesOfFit("Fit", m_Storage); + CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit", + nodes->Size() == 2); + CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode.GetPointer()) != nodes->end()); + CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2.GetPointer()) != nodes->end()); + + nodes = mitk::modelFit::GetNodesOfFit("FitLegacy", m_Storage); + CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for FitLegacy", + nodes->Size() == 2); + CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode_legacy.GetPointer()) != nodes->end()); + CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2_legacy.GetPointer()) != nodes->end()); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit2", + mitk::modelFit::GetNodesOfFit("Fit2", m_Storage)->Size() == 1); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unkown fits.", + mitk::modelFit::GetNodesOfFit("unkown_fit", m_Storage)->Size() == 0); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for illegal calls.", + mitk::modelFit::GetNodesOfFit("unkown_fit", nullptr).IsNull()); + } + + void CheckGetFitUIDsOfNode() + { + auto testNode = m_Storage->GetNamedNode("Input"); + mitk::modelFit::NodeUIDSetType uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.", + uidSet.size() == 2 && + uidSet.find("Fit") != uidSet.end() && + uidSet.find("FitLegacy") != uidSet.end()); + + testNode = m_Storage->GetNamedNode("AnotherInput"); + uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.", + uidSet.size() == 1 && + uidSet.find("Fit2") != uidSet.end()); + + uidSet = mitk::modelFit::GetFitUIDsOfNode(nullptr, m_Storage); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid node.", + uidSet.size() == 0); + + uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, nullptr); + + CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid storage.", + uidSet.size() == 0); + } +}; - uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode,nullptr); - MITK_TEST_CONDITION_REQUIRED(uidSet.size() == 0, - "Testing if GetFitUIDsOfNode works correctly with invalid storage."); - - MITK_TEST_END() -} +MITK_TEST_SUITE_REGISTRATION(mitkModelFitInfo) \ No newline at end of file diff --git a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp b/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp similarity index 68% copy from Modules/Core/test/mitkSourceImageRelationRuleTest.cpp copy to Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp index 006153952b..7fb29e0d42 100644 --- a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp +++ b/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp @@ -1,944 +1,732 @@ /*============================================================================ 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 "mitkSourceImageRelationRule.h" +#include "mitkModelFitResultRelationRule.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkPropertyNameHelper.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkPropertyNameHelper.h" +#include "mitkModelFitConstants.h" +#include "mitkUIDGenerator.h" #include -class mitkSourceImageRelationRuleTestSuite : public mitk::TestFixture +class mitkModelFitResultRelationRuleTestSuite : public mitk::TestFixture { - CPPUNIT_TEST_SUITE(mitkSourceImageRelationRuleTestSuite); + CPPUNIT_TEST_SUITE(mitkModelFitResultRelationRuleTestSuite); + MITK_TEST(ConstructionAndGetter); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); - MITK_TEST(GetDestinationsDetector); - MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); - MITK_TEST(Connect_abstract); - MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: - mitk::SourceImageRelationRule::Pointer rule; - mitk::SourceImageRelationRule::Pointer abstractRule; + mitk::ModelFitResultRelationRule::Pointer rule; mitk::Image::Pointer unRelated; mitk::DataNode::Pointer unRelated_Node; mitk::Image::Pointer source_implicit_1; mitk::DataNode::Pointer source_implicit_1_Node; mitk::Image::Pointer source_Data_1; mitk::DataNode::Pointer source_Data_1_Node; mitk::Image::Pointer source_idOnly_1; mitk::DataNode::Pointer source_idOnly_1_Node; mitk::Image::Pointer source_1; mitk::DataNode::Pointer source_1_Node; + mitk::Image::Pointer source_legacy_1; + mitk::DataNode::Pointer source_legacy_1_Node; mitk::Image::Pointer source_otherRule; mitk::DataNode::Pointer source_otherRule_Node; mitk::Image::Pointer source_otherPurpose; mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1_Node; mitk::Image::Pointer dest_1; mitk::DataNode::Pointer dest_2_Node; mitk::Image::Pointer dest_2; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } std::vector GetReferenceSequenceIndices(const mitk::IPropertyProvider * source, const mitk::IPropertyProvider * destination) const { std::vector result; auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } mitk::PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = source->GetPropertyKeys(); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (*refUIDProp == *destInstanceUIDProp) { mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key); result.push_back(std::to_string(finding.GetNode(2).selection)); } } } return result; }; void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID)); mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID)); mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose)); } bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath)); if (prop->GetValueAsString() != instanceUID) { return false; } mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath)); if (prop->GetValueAsString() != classUID) { return false; } mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); if (prop->GetValueAsString() != purpose) { return false; } return true; } public: void setUp() override { auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018); auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016); - rule = mitk::SourceImageRelationRule::New("Test"); - - abstractRule = mitk::SourceImageRelationRule::New(); + rule = mitk::ModelFitResultRelationRule::New(); unRelated = mitk::Image::New(); unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated")); unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); unRelated_Node = mitk::DataNode::New(); unRelated_Node->SetData(unRelated); dest_1_Node = mitk::DataNode::New(); dest_1_Node->SetName("dest_1"); dest_1 = mitk::Image::New(); dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1")); dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); + dest_1->GetTimeGeometry()->Expand(2); dest_1_Node->SetData(dest_1); - + //support of legacy mode + mitk::UIDGenerator generator; + auto legacyUID = generator.GetUID(); + dest_1->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(legacyUID)); dest_2_Node = mitk::DataNode::New(); dest_2_Node->SetName("dest_2"); dest_2 = mitk::Image::New(); dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2")); dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); + dest_2->GetTimeGeometry()->Expand(3); dest_2_Node->SetData(dest_2); source_implicit_1 = mitk::Image::New(); - SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Model fit input", 0); source_implicit_1_Node = mitk::DataNode::New(); source_implicit_1_Node->SetData(source_implicit_1); source_idOnly_1 = mitk::Image::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_idOnly_1_Node = mitk::DataNode::New(); source_idOnly_1_Node->SetData(source_idOnly_1); source_Data_1 = mitk::Image::New(); - SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Model fit input", 0); SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.ruleID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); name = "MITK.Relations.2.ruleID"; source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_Data_1_Node = mitk::DataNode::New(); source_Data_1_Node->SetData(source_Data_1); source_1 = mitk::Image::New(); - SetDICOMReferenceInfo(source_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_1, "dest_1", "image", "Model fit input", 0); SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3")); name = "MITK.Relations.1.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8")); name = "MITK.Relations.2.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); source_1_Node = mitk::DataNode::New(); source_1_Node->SetData(source_1); + source_legacy_1 = mitk::Image::New(); + source_legacy_1->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(legacyUID.c_str())); + source_legacy_1_Node = mitk::DataNode::New(); + source_legacy_1_Node->SetData(source_legacy_1); + source_otherRule = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherRule_Node = mitk::DataNode::New(); source_otherRule_Node->SetData(source_otherRule); source_otherPurpose = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_otherPurpose_Node = mitk::DataNode::New(); source_otherPurpose_Node->SetData(source_otherPurpose); } void tearDown() override {} + void ConstructionAndGetter() + { + CPPUNIT_ASSERT(!rule->IsAbstract()); + + CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Model fit input")); + CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("Model fit result relation")); + CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("fit result")); + CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image")); + } + void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node)); CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1)); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); + CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::Image::New())); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_Data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); + CPPUNIT_ASSERT(rule->IsSource(source_legacy_1)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node)); - - - CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); - - CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose)); - - CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_1_Node)); - - CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose_Node)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty()); auto uids = rule->GetExistingRelations(source_implicit_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); - - uids = abstractRule->GetExistingRelations(source_implicit_1); - CPPUNIT_ASSERT(uids.size() == 1); - CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); - - uids = abstractRule->GetExistingRelations(source_idOnly_1); + uids = rule->GetExistingRelations(source_legacy_1); CPPUNIT_ASSERT(uids.size() == 1); - CPPUNIT_ASSERT(uids.front() == "uid1"); - - uids = abstractRule->GetExistingRelations(source_Data_1); - CPPUNIT_ASSERT(uids.size() == 2); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); - - uids = abstractRule->GetExistingRelations(source_1); - CPPUNIT_ASSERT(uids.size() == 2); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); - - uids = abstractRule->GetExistingRelations(source_otherPurpose); - CPPUNIT_ASSERT(uids.size() == 1); - CPPUNIT_ASSERT(uids.front() == "uid9"); + CPPUNIT_ASSERT(uids.front() == "model.fit.input.image.legacy.relation"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); - - CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); - CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); - CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); - CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherPurpose, dest_1).front() == "uid9"); - - CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); - uids = abstractRule->GetRelationUIDs(source_Data_1, dest_1); - CPPUNIT_ASSERT(uids.size() == 1); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); - uids = abstractRule->GetRelationUIDs(source_1, dest_1); - CPPUNIT_ASSERT(uids.size() == 1); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); - uids = abstractRule->GetRelationUIDs(source_1, dest_2); - CPPUNIT_ASSERT(uids.size() == 1); - CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_legacy_1, dest_1).front() == "model.fit.input.image.legacy.relation"); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); + CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); - - - auto predicate2 = abstractRule->GetConnectedSourcesDetector(); - - CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); - CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_Data_1_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_1_Node)); - - CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_otherPurpose_Node)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node)); predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node)); - - predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); - - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); - - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - - predicate = abstractRule->GetSourcesDetector(dest_1); - - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); - - CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - } - - void GetDestinationsDetector() - { - CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", - rule->GetDestinationsDetector(nullptr), - itk::ExceptionObject); - - auto predicate = rule->GetDestinationsDetector(source_otherRule); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - - predicate = rule->GetDestinationsDetector(source_otherPurpose); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - - predicate = rule->GetDestinationsDetector(source_implicit_1); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - - predicate = rule->GetDestinationsDetector(source_Data_1); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::ID); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - - predicate = rule->GetDestinationsDetector(source_idOnly_1); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - - predicate = rule->GetDestinationsDetector(source_1); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); - predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); - predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); - predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); - - predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - } - - void GetDestinationDetector() - { - CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", - rule->GetDestinationDetector(nullptr, "uid1"), - itk::ExceptionObject); - - CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", - rule->GetDestinationDetector(source_1, "invalid uid"), - itk::ExceptionObject); - - auto predicate = rule->GetDestinationDetector(source_1, "uid3"); - CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_implicit_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Model fit input", 0)); // check upgrade and reuse of an Data connection (no new relation should be generated). CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_Data_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(relUID.size() == 1); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Model fit input", 0)); // check actualization of an id only connection rule->Connect(source_idOnly_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check actualization of an existing connection rule->Connect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0)); // check creation of an new connection rule->Connect(unRelated, dest_1); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.", rule->GetExistingRelations(unRelated).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Model fit input", 0)); // check creation of a 2nd connection of the same purpose rule->Connect(unRelated, dest_2); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.", rule->GetExistingRelations(unRelated).size() == 2); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); name = "MITK.Relations.2.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID()); name = "MITK.Relations.2.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.2.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Test", 1)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Model fit input", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Model fit input", 1)); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0)); //check if index correction is correct, when disconnecting rule->Connect(source_1, dest_2); rule->Connect(source_1, unRelated); rule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4")); - CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1)); - CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Test", 2)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Model fit input", 2)); std::string name = "MITK.Relations.4.destinationUID"; auto prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID()); name = "MITK.Relations.4.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2"); rule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1")); } - - void Connect_abstract() - { - CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", - abstractRule->Connect(nullptr, dest_1), - itk::ExceptionObject); - - CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", - abstractRule->Connect(source_1, nullptr), - itk::ExceptionObject); - } - - void Disconnect_abstract() - { - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); - abstractRule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - abstractRule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); - CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); - - abstractRule->Disconnect(source_otherPurpose, dest_1); - CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherPurpose, "1")); - } - }; -MITK_TEST_SUITE_REGISTRATION(mitkSourceImageRelationRule) +MITK_TEST_SUITE_REGISTRATION(mitkModelFitResultRelationRule) diff --git a/Modules/ModelFit/test/mitkModelFitUIDHelperTest.cpp b/Modules/ModelFit/test/mitkModelFitUIDHelperTest.cpp deleted file mode 100644 index 19dcf8c8d0..0000000000 --- a/Modules/ModelFit/test/mitkModelFitUIDHelperTest.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include -#include "mitkTestingMacros.h" -#include "mitkDataNode.h" -#include "mitkImage.h" -#include "mitkStandaloneDataStorage.h" - -#include "mitkModelFitUIDHelper.h" - -int mitkModelFitUIDHelperTest(int /*argc*/, char*[] /*argv[]*/) -{ - MITK_TEST_BEGIN("mitkModelFitUIDHelperTest") - - mitk::DataNode::Pointer node1 = mitk::DataNode::New(); - node1->SetData(mitk::Image::New()); - mitk::DataNode::Pointer node2 = mitk::DataNode::New(); - node2->SetData(mitk::Image::New()); - - mitk::DataNode* nullNode = nullptr; - mitk::BaseData* nullData = nullptr; - - MITK_TEST_FOR_EXCEPTION(mitk::Exception, mitk::EnsureModelFitUID(nullNode)); - MITK_TEST_CONDITION_REQUIRED(mitk::CheckModelFitUID(nullNode,"testUID") == false, - "Testing if CheckModelFitUID fails of null pointer is passed."); - MITK_TEST_FOR_EXCEPTION(mitk::Exception, mitk::EnsureModelFitUID(nullData)); - MITK_TEST_CONDITION_REQUIRED(mitk::CheckModelFitUID(nullData, "testUID") == false, - "Testing if CheckModelFitUID fails of null pointer is passed."); - - mitk::NodeUIDType uid = mitk::EnsureModelFitUID(node1); - - MITK_TEST_CONDITION_REQUIRED(mitk::CheckModelFitUID(node1,"testUID") == false, - "Testing if CheckModelFitUID fails of wrong UID is passed."); - MITK_TEST_CONDITION_REQUIRED(mitk::CheckModelFitUID(node1, uid) == true, - "Testing if CheckModelFitUID succeeds if correct UID is passed."); - - mitk::NodeUIDType uid2ndCall = mitk::EnsureModelFitUID(node1); - - MITK_TEST_CONDITION_REQUIRED(uid == uid2ndCall, - "Testing if EnsureModelFitUID does not create new UIDs on multiple calls."); - - mitk::NodeUIDType uid2 = mitk::EnsureModelFitUID(node2); - - MITK_TEST_CONDITION_REQUIRED(uid != uid2, - "Testing if EnsureModelFitUID does create different UIDs for different nodes."); - - mitk::StandaloneDataStorage::Pointer storage = mitk::StandaloneDataStorage::New(); - storage->Add(node1); - storage->Add(node2); - - MITK_TEST_CONDITION_REQUIRED(node1 == GetNodeByModelFitUID(storage,uid), - "Testing if GetNodeByModelFitUID finds node 1."); - - MITK_TEST_CONDITION_REQUIRED(node2 == GetNodeByModelFitUID(storage,uid2), - "Testing if GetNodeByModelFitUID finds node 2."); - - MITK_TEST_CONDITION_REQUIRED(GetNodeByModelFitUID(storage,"unkown_uid").IsNull(), - "Testing if GetNodeByModelFitUID returns NULL for unkown UID."); - - MITK_TEST_END() -} diff --git a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp index 03af6417c2..5a44e08d26 100644 --- a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp +++ b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp @@ -1,890 +1,890 @@ /*============================================================================ 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. ============================================================================*/ // std includes #include // itk includes #include "itksys/SystemTools.hxx" // CTK includes #include "mitkCommandLineParser.h" // MITK includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include std::string inFilename; std::string outFileName; std::string maskFileName; std::string aifMaskFileName; std::string aifImageFileName; mitk::Image::Pointer image; mitk::Image::Pointer mask; mitk::Image::Pointer aifImage; mitk::Image::Pointer aifMask; bool useConstraints(false); bool verbose(false); bool roibased(false); bool preview(false); std::string modelName; float aifHematocritLevel(0); float brixInjectionTime(0); const std::string MODEL_NAME_2SL = "2SL"; const std::string MODEL_NAME_3SL = "3SL"; const std::string MODEL_NAME_descriptive = "descriptive"; const std::string MODEL_NAME_tofts = "tofts"; const std::string MODEL_NAME_2CX = "2CX"; void onFitEvent(::itk::Object* caller, const itk::EventObject & event, void* /*data*/) { itk::ProgressEvent progressEvent; if (progressEvent.CheckEvent(&event)) { mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast(caller); std::cout <GetProgress()*100 << "% "; } } void setupParser(mitkCommandLineParser& parser) { // set general information about your MiniApp parser.setCategory("Dynamic Data Analysis Tools"); parser.setTitle("MR Perfusion"); parser.setDescription("MiniApp that allows to fit MRI perfusion models and generates the according parameter maps. IMPORTANT!!!: The app assumes that the input images (signal and AIF) are concentration images. If your images do not hold this assumption, convert the image date before using this app (e.g. by using the signal-to-concentration-converter mini app."); parser.setContributor("DKFZ MIC"); //! [create parser] //! [add arguments] // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Model parameters"); parser.addArgument( "model", "l", mitkCommandLineParser::String, "Model function", "Model that should be used to fit the concentration signal. Options are: \""+MODEL_NAME_descriptive+"\" (descriptive pharmacokinetic Brix model),\""+MODEL_NAME_3SL+"\" (three step linear model), \""+MODEL_NAME_tofts+"\" (extended tofts model) or \""+MODEL_NAME_2CX+"\" (two compartment exchange model).", us::Any(std::string(MODEL_NAME_tofts))); parser.addArgument( "injectiontime", "j", mitkCommandLineParser::Float, "Injection time [min]", "Injection time of the bolus. This information is needed for the descriptive pharmacokinetic Brix model.", us::Any()); parser.endGroup(); parser.beginGroup("Required I/O parameters"); parser.addArgument( "input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file template", "where to save the output parameter images. The specified path will be used as template to determine the format (via extension) and the name \"root\". For each parameter a suffix will be added to the name.", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("AIF parameters"); parser.addArgument( "aifmask", "n", mitkCommandLineParser::File, "AIF mask file", "Mask that defines the spatial image region that should be used as AIF for models that need one. Must have the same geometry as the AIF input image!", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument( "aifimage", "a", mitkCommandLineParser::File, "AIF image file", "3D+t image that defines the image that containes the AIF signal. If this flag is not set and the model needs a AIF, the CLI will assume that the AIF is encoded in the normal image. Must have the same geometry as the AIF mask!", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument( "hematocrit", "h", mitkCommandLineParser::Float, "Hematocrit Level", "Value needed for correct AIF computation. Only needed if model needs an AIF. Default value is 0.45.", us::Any(0.45)); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be fitted. Must have the same geometry as the input image!", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output"); parser.addArgument( "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intesity signal over the ROI before fitting it. If this mode is used a mask must be specified."); parser.addArgument( "constraints", "c", mitkCommandLineParser::Bool, "Constraints", "Indicates if constraints should be used for the fitting (if flag is set the default contraints will be used.).", us::Any(false)); parser.addArgument( "preview", "p", mitkCommandLineParser::Bool, "Preview outputs", "The application previews the outputs (filename, type) it would produce with the current settings."); parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.endGroup(); //! [add arguments] } bool configureApplicationSettings(std::map parsedArgs) { if (parsedArgs.size() == 0) return false; // parse, cast and set required arguments modelName = MODEL_NAME_tofts; if (parsedArgs.count("model")) { modelName = us::any_cast(parsedArgs["model"]); } inFilename = us::any_cast(parsedArgs["input"]); outFileName = us::any_cast(parsedArgs["output"]); if (parsedArgs.count("mask")) { maskFileName = us::any_cast(parsedArgs["mask"]); } if (parsedArgs.count("aifimage")) { aifImageFileName = us::any_cast(parsedArgs["aifimage"]); } if (parsedArgs.count("aifmask")) { aifMaskFileName = us::any_cast(parsedArgs["aifmask"]); } verbose = false; if (parsedArgs.count("verbose")) { verbose = us::any_cast(parsedArgs["verbose"]); } preview = false; if (parsedArgs.count("preview")) { preview = us::any_cast(parsedArgs["preview"]); } roibased = false; if (parsedArgs.count("roibased")) { roibased = us::any_cast(parsedArgs["roibased"]); } useConstraints = false; if (parsedArgs.count("constraints")) { useConstraints = us::any_cast(parsedArgs["constraints"]); } aifHematocritLevel = 0.45; if (parsedArgs.count("hematocrit")) { aifHematocritLevel = us::any_cast(parsedArgs["hematocrit"]); } brixInjectionTime = 0.0; if (parsedArgs.count("injectiontime")) { brixInjectionTime = us::any_cast(parsedArgs["injectiontime"]); } return true; } mitk::ModelFitFunctorBase::Pointer createDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer, const mitk::ModelFactoryBase* modelFactory) { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (useConstraints) { fitFunctor->SetConstraintChecker(modelFactory->CreateDefaultConstraints().GetPointer()); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(true); return fitFunctor.GetPointer(); } /**Helper that ensures that the mask (if it exists) is always 3D image. If the mask is originally an 4D image, the first time step will be used.*/ mitk::Image::Pointer getMask3D() { mitk::Image::Pointer result; if (mask.IsNotNull()) { result = mask; //mask settings if (mask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit."; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(mask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); result = maskedImageTimeSelector->GetOutput(); } } return result; } void getAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (aifMask.IsNotNull()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(aifHematocritLevel); std::cout << "AIF hematocrit level: " << aifHematocritLevel << std::endl; mitk::Image::Pointer selectedAIFMask = aifMask; //mask settings if (aifMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit."; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(aifMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); aifMask = maskedImageTimeSelector->GetOutput(); } aifGenerator->SetMask(aifMask); mitk::Image::Pointer selectedAIFImage = image; //image settings if (aifImage.IsNotNull()) { selectedAIFImage = aifImage; } aifGenerator->SetDynamicImage(selectedAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. AIF mask was not specified or correctly loaded."; } } void generateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelParameterizer::New(); mitk::Image::Pointer mask3D = getMask3D(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(brixInjectionTime); std::cout << "Injection time [min]: " << brixInjectionTime << std::endl; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage; mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage); modelParameterizer->SetBaseImage(baseImage); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (mask3D.IsNotNull()) { fitGenerator->SetMask(mask3D); - roiUID = mitk::EnsureModelFitUID(mask); + roiUID = mask->GetUID(); } fitGenerator->SetDynamicImage(image); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID); } void generateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::Image::Pointer mask3D = getMask3D(); if (mask3D.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(mask3D); signalGenerator->SetDynamicImage(image); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(brixInjectionTime); std::cout << "Injection time [min]: " << brixInjectionTime << std::endl; modelParameterizer->SetBaseValue(roiSignal[0]); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(mask3D); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(mask); + std::string roiUID = mask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::Image::Pointer mask3D = getMask3D(); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (mask3D.IsNotNull()) { fitGenerator->SetMask(mask3D); - roiUID = mitk::EnsureModelFitUID(mask); + roiUID = mask->GetUID(); } fitGenerator->SetDynamicImage(image); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID); } template void GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::Image::Pointer mask3D = getMask3D(); if (mask3D.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(mask3D); signalGenerator->SetDynamicImage(image); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(mask3D); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(mask); + std::string roiUID = mask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void generateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; getAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); mitk::Image::Pointer mask3D = getMask3D(); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (mask3D.IsNotNull()) { fitGenerator->SetMask(mask3D); - roiUID = mitk::EnsureModelFitUID(mask); + roiUID = mask->GetUID(); } fitGenerator->SetDynamicImage(image); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } template void generateAIFbasedModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::Image::Pointer mask3D = getMask3D(); if (mask3D.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; getAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(mask3D); signalGenerator->SetDynamicImage(image); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(mask3D); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(mask); + std::string roiUID = mask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); infoSignal.clear(); for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } void storeResultImage(const std::string& name, mitk::Image* image, mitk::modelFit::Parameter::Type nodeType, const mitk::modelFit::ModelFitInfo* modelFitInfo) { mitk::modelFit::SetModelFitDataProperties(image, name, nodeType, modelFitInfo); std::string ext = ::itksys::SystemTools::GetFilenameLastExtension(outFileName); std::string dir = itksys::SystemTools::GetFilenamePath(outFileName); dir = itksys::SystemTools::ConvertToOutputPath(dir); std::string rootName = itksys::SystemTools::GetFilenameWithoutLastExtension(outFileName); std::string fileName = rootName + "_" + name + ext; std::vector pathElements; pathElements.push_back(dir); pathElements.push_back(fileName); std::string fullOutPath = itksys::SystemTools::ConvertToOutputPath(dir + "/" + fileName); mitk::IOUtil::Save(image, fullOutPath); std::cout << "Store result (parameter: "<(fitSession, generator); } else { GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (is2SLFactory) { std::cout << "Model: two step linear model" << std::endl; if (!roibased) { GenerateLinearModelFit_PixelBased(fitSession, generator); } else { GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (isToftsFactory) { std::cout << "Model: extended tofts model" << std::endl; if (!roibased) { generateAIFbasedModelFit_PixelBased(fitSession, generator); } else { generateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (is2CXMFactory) { std::cout << "Model: two compartment exchange model" << std::endl; if (!roibased) { generateAIFbasedModelFit_PixelBased(fitSession, generator); } else { generateAIFbasedModelFit_ROIBased(fitSession, generator); } } else { std::cerr << "ERROR. Model flag is unknown. Given flag: " << modelName << std::endl; } } void doFitting() { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; ::itk::CStyleCommand::Pointer command = ::itk::CStyleCommand::New(); command->SetCallback(onFitEvent); createFitGenerator(fitSession, generator); if (generator.IsNotNull() ) { std::cout << "Started fitting process..." << std::endl; generator->AddObserver(::itk::AnyEvent(), command); generator->Generate(); std::cout << std::endl << "Finished fitting process" << std::endl; mitk::storeModelFitGeneratorResults(outFileName, generator, fitSession); } else { mitkThrow() << "Fitting error! Could not initialize fitting job."; } } void doPreview() { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; createFitGenerator(fitSession, generator); if (generator.IsNotNull()) { mitk::previewModelFitGeneratorResults(outFileName, generator); } else { mitkThrow() << "Fitting error! Could not initialize fitting job."; } } int main(int argc, char* argv[]) { mitkCommandLineParser parser; setupParser(parser); const std::map& parsedArgs = parser.parseArguments(argc, argv); mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (classic config)" }, { "MITK DICOM Reader" }); if (!configureApplicationSettings(parsedArgs)) { return EXIT_FAILURE; }; // Show a help message if (parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } //! [do processing] try { image = mitk::IOUtil::Load(inFilename, &readerFilterFunctor); std::cout << "Input: " << inFilename << std::endl; if (!maskFileName.empty()) { mask = mitk::IOUtil::Load(maskFileName, &readerFilterFunctor); std::cout << "Mask: " << maskFileName << std::endl; } else { std::cout << "Mask: none" << std::endl; } if (modelName != MODEL_NAME_descriptive && modelName != MODEL_NAME_3SL) { if (!aifMaskFileName.empty()) { aifMask = mitk::IOUtil::Load(aifMaskFileName, &readerFilterFunctor); std::cout << "AIF mask: " << aifMaskFileName << std::endl; } else { mitkThrow() << "Error. Cannot fit. Choosen model needs an AIF. Please specify AIF mask (--aifmask)."; } if (!aifImageFileName.empty()) { aifImage = mitk::IOUtil::Load(aifImageFileName, &readerFilterFunctor); std::cout << "AIF image: " << aifImageFileName << std::endl; } else { std::cout << "AIF image: none (using signal image)" << std::endl; } } if (roibased && mask.IsNull()) { mitkThrow() << "Error. Cannot fit. Please specify mask if you select roi based fitting."; } std::cout << "Style: "; if (roibased) { std::cout << "ROI based"; } else { std::cout << "pixel based"; } std::cout << std::endl; if (preview) { doPreview(); } else { doFitting(); } std::cout << "Processing finished." << std::endl; return EXIT_SUCCESS; } catch (const itk::ExceptionObject& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (const std::exception& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (...) { MITK_ERROR << "Unexpected error encountered."; return EXIT_FAILURE; } } diff --git a/Modules/Pharmacokinetics/cmdapps/MRSignal2ConcentrationMiniApp.cpp b/Modules/Pharmacokinetics/cmdapps/MRSignal2ConcentrationMiniApp.cpp index 60a0b67c80..bd517b3abb 100644 --- a/Modules/Pharmacokinetics/cmdapps/MRSignal2ConcentrationMiniApp.cpp +++ b/Modules/Pharmacokinetics/cmdapps/MRSignal2ConcentrationMiniApp.cpp @@ -1,289 +1,287 @@ /*============================================================================ 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. ============================================================================*/ // std includes #include // itk includes #include "itksys/SystemTools.hxx" // CTK includes #include "mitkCommandLineParser.h" // MITK includes #include #include #include #include -#include #include std::string inFilename; std::string outFileName; mitk::Image::Pointer image; bool verbose(false); bool t1_absolute(false); bool t1_relative(false); bool t1_flash(false); bool t2(false); float k(1.0); float te(0); float rec_time(0); float relaxivity(0); float rel_time(0); void setupParser(mitkCommandLineParser& parser) { // set general information about your MiniApp parser.setCategory("Dynamic Data Analysis Tools"); parser.setTitle("MR Signal to Concentration Converter"); parser.setDescription("MiniApp that allows to convert a T1 or T2 signal image into a concentration image for perfusion analysis."); parser.setContributor("DKFZ MIC"); //! [create parser] //! [add arguments] // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Required I/O parameters"); parser.addArgument( "input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file", "where to save the output concentration image.", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("Conversion parameters"); parser.addArgument( "t1-absolute", "", mitkCommandLineParser::Bool, "T1 absolute signal enhancement", "Activate conversion for T1 absolute signal enhancement."); parser.addArgument( "t1-relative", "", mitkCommandLineParser::Bool, "T1 relative signal enhancement", "Activate conversion for T1 relative signal enhancement."); parser.addArgument( "t1-flash", "", mitkCommandLineParser::Bool, "T1 turbo flash", "Activate specific conversion for T1 turbo flash sequences."); parser.addArgument( "t2", "", mitkCommandLineParser::Bool, "T2 signal conversion", "Activate conversion for T2 signal enhancement to concentration."); parser.addArgument( "k", "k", mitkCommandLineParser::Float, "Conversion factor k", "Needed for the following conversion modes: T1-absolute, T1-relative, T2. Default value is 1.", us::Any(1)); parser.addArgument( "recovery-time", "", mitkCommandLineParser::Float, "Recovery time", "Needed for the following conversion modes: T1-flash."); parser.addArgument( "relaxivity", "", mitkCommandLineParser::Float, "Relaxivity", "Needed for the following conversion modes: T1-flash."); parser.addArgument( "relaxation-time", "", mitkCommandLineParser::Float, "Relaxation time", "Needed for the following conversion modes: T1-flash."); parser.addArgument( "te", "", mitkCommandLineParser::Float, "Echo time TE", "Needed for the following conversion modes: T2.", us::Any(1)); parser.beginGroup("Optional parameters"); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output"); parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.endGroup(); //! [add arguments] } bool configureApplicationSettings(std::map parsedArgs) { if (parsedArgs.size() == 0) return false; inFilename = us::any_cast(parsedArgs["input"]); outFileName = us::any_cast(parsedArgs["output"]); verbose = false; if (parsedArgs.count("verbose")) { verbose = us::any_cast(parsedArgs["verbose"]); } t1_absolute = false; if (parsedArgs.count("t1-absolute")) { t1_absolute = us::any_cast(parsedArgs["t1-absolute"]); } t1_relative = false; if (parsedArgs.count("t1-relative")) { t1_relative = us::any_cast(parsedArgs["t1-relative"]); } t1_flash = false; if (parsedArgs.count("t1-flash")) { t1_flash = us::any_cast(parsedArgs["t1-flash"]); } t2 = false; if (parsedArgs.count("t2")) { t2 = us::any_cast(parsedArgs["t2"]); } k = 0.0; if (parsedArgs.count("k")) { k = us::any_cast(parsedArgs["k"]); } relaxivity = 0.0; if (parsedArgs.count("relaxivity")) { relaxivity = us::any_cast(parsedArgs["relaxivity"]); } rec_time = 0.0; if (parsedArgs.count("recovery-time")) { rec_time = us::any_cast(parsedArgs["recovery-time"]); } rel_time = 0.0; if (parsedArgs.count("relaxation-time")) { rel_time = us::any_cast(parsedArgs["relaxation-time"]); } te = 0.0; if (parsedArgs.count("te")) { te = us::any_cast(parsedArgs["te"]); } //consistency checks int modeCount = 0; if (t1_absolute) ++modeCount; if (t1_flash) ++modeCount; if (t1_relative) ++modeCount; if (t2) ++modeCount; if (modeCount==0) { mitkThrow() << "Invalid program call. Please select the type of conversion."; } if (modeCount >1) { mitkThrow() << "Invalid program call. Please select only ONE type of conversion."; } if (!k && (t2 || t1_absolute || t1_relative)) { mitkThrow() << "Invalid program call. Please set 'k', if you use t1-absolute, t1-relative or t2."; } if (!te && t2) { mitkThrow() << "Invalid program call. Please set 'te', if you use t2 mode."; } if ((!rec_time||!rel_time||!relaxivity) && t1_flash) { mitkThrow() << "Invalid program call. Please set 'recovery-time', 'relaxation-time' and 'relaxivity', if you use t1-flash mode."; } return true; } void doConversion() { mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); concentrationGen->SetDynamicImage(image); concentrationGen->SetisTurboFlashSequence(t1_flash); concentrationGen->SetAbsoluteSignalEnhancement(t1_absolute); concentrationGen->SetRelativeSignalEnhancement(t1_relative); concentrationGen->SetisT2weightedImage(t2); if (t1_flash) { concentrationGen->SetRecoveryTime(rec_time); concentrationGen->SetRelaxationTime(rel_time); concentrationGen->SetRelaxivity(relaxivity); } else if (t2) { concentrationGen->SetT2Factor(k); concentrationGen->SetT2EchoTime(te); } else { concentrationGen->SetFactor(k); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); - mitk::EnsureModelFitUID(concentrationImage); mitk::IOUtil::Save(concentrationImage, outFileName); std::cout << "Store result: " << outFileName << std::endl; } int main(int argc, char* argv[]) { mitkCommandLineParser parser; setupParser(parser); const std::map& parsedArgs = parser.parseArguments(argc, argv); if (!configureApplicationSettings(parsedArgs)) { return EXIT_FAILURE; }; mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (classic config)" }, { "MITK DICOM Reader" }); // Show a help message if (parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } //! [do processing] try { image = mitk::IOUtil::Load(inFilename, &readerFilterFunctor); std::cout << "Input: " << inFilename << std::endl; doConversion(); std::cout << "Processing finished." << std::endl; return EXIT_SUCCESS; } catch (const itk::ExceptionObject& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (const std::exception& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (...) { MITK_ERROR << "Unexpected error encountered."; return EXIT_FAILURE; } } diff --git a/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp b/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp index d61cc38065..3a8dfb2d14 100644 --- a/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp @@ -1,642 +1,639 @@ /*============================================================================ 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 "GenericDataFittingView.h" #include "mitkWorkbenchUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include #include const std::string GenericDataFittingView::VIEW_ID = "org.mitk.gui.qt.fit.genericfitting"; void GenericDataFittingView::SetFocus() { m_Controls.btnModelling->setFocus(); } void GenericDataFittingView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); this->InitModelComboBox(); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); //Gerneric setting m_Controls.groupGeneric->hide(); m_Controls.labelFormulaInfo->hide(); connect(m_Controls.editFormula, SIGNAL(textChanged(const QString&)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkFormulaInfo, SIGNAL(toggled(bool)), m_Controls.labelFormulaInfo, SLOT(setVisible(bool))); connect(m_Controls.nrOfParams, SIGNAL(valueChanged(int)), this, SLOT(OnNrOfParamsChanged())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); UpdateGUIControls(); } void GenericDataFittingView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool isGenericFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupGeneric->setVisible(isGenericFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupGeneric->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); } std::string GenericDataFittingView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string GenericDataFittingView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void GenericDataFittingView::OnNrOfParamsChanged() { PrepareFitConfiguration(); UpdateGUIControls(); } void GenericDataFittingView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } UpdateGUIControls(); } bool GenericDataFittingView::IsGenericParamFactorySelected() const { return dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; } void GenericDataFittingView::PrepareFitConfiguration() { if (m_selectedModelFactory) { mitk::ModelBase::ParameterNamesType paramNames = m_selectedModelFactory->GetParameterNames(); unsigned int nrOfPools = this->m_Controls.nrOfParams->value(); //init values if (this->IsGenericParamFactorySelected()) { mitk::modelFit::ModelFitInfo::Pointer fitInfo = mitk::modelFit::ModelFitInfo::New(); fitInfo->staticParamMap.Add(mitk::GenericParamModel::NAME_STATIC_PARAMETER_number, { static_cast(nrOfPools) }); auto parameterizer = m_selectedModelFactory->CreateParameterizer(fitInfo); paramNames = parameterizer->GetParameterNames(); m_Controls.initialValuesManager->setInitialValues(paramNames, parameterizer->GetDefaultInitialParameterization()); } else { m_Controls.initialValuesManager->setInitialValues(paramNames, this->m_selectedModelFactory->GetDefaultInitialParameterization()); } //constraints this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, paramNames); } }; void GenericDataFittingView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isLinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isGenericFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isT2DecayFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isLinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isGenericFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isT2DecayFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; UpdateGUIControls(); DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void GenericDataFittingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& selectedNodes) { m_selectedNode = nullptr; m_selectedImage = nullptr; m_selectedMaskNode = nullptr; m_selectedMask = nullptr; m_Controls.masklabel->setText("No (valid) mask selected."); m_Controls.timeserieslabel->setText("No (valid) series selected."); QList nodes = selectedNodes; mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer maskPredicate = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); if (nodes.size() > 0 && isImage->CheckNode(nodes.front())) { this->m_selectedNode = nodes.front(); this->m_selectedImage = dynamic_cast(this->m_selectedNode->GetData()); m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); nodes.pop_front(); } if (nodes.size() > 0 && maskPredicate->CheckNode(nodes.front())) { this->m_selectedMaskNode = nodes.front(); this->m_selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str()); } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } UpdateGUIControls(); } bool GenericDataFittingView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isGenericFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; if (isGenericFactory) { ok = !m_Controls.editFormula->text().isEmpty(); } } else { ok = false; } return ok; } void GenericDataFittingView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::ValueBasedParameterizationDelegate::Pointer paramDelegate = mitk::ValueBasedParameterizationDelegate::New(); paramDelegate->SetInitialParameterization(m_Controls.initialValuesManager->getInitialValues()); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } mitk::GenericParamModelParameterizer* genericParameterizer = dynamic_cast(parameterizer); if (genericParameterizer) { genericParameterizer->SetFunctionString(m_Controls.editFormula->text().toStdString()); } } template void GenericDataFittingView::GenerateModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); auto genericParameterizer = dynamic_cast(modelParameterizer.GetPointer()); if (genericParameterizer) { genericParameterizer->SetNumberOfParameters(this->m_Controls.nrOfParams->value()); } this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); - roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + roiUID = m_selectedMask->GetUID(); } - mitk::EnsureModelFitUID(this->m_selectedNode); - fitGenerator->SetDynamicImage(this->m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } template void GenericDataFittingView::GenerateModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); auto genericParameterizer = dynamic_cast(modelParameterizer.GetPointer()); if (genericParameterizer) { genericParameterizer->SetNumberOfParameters(this->m_Controls.nrOfParams->value()); } this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); - mitk::EnsureModelFitUID(this->m_selectedNode); - std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } void GenericDataFittingView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { QString message = "Fitting Data Set . . ."; m_Controls.infoBox->append(message); ///////////////////////// //create job and put it into the thread pool ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } GenericDataFittingView::GenericDataFittingView() : m_FittingInProgress(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::LinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::GenericParamModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::T2DecayModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); this->m_IsNotABinaryImagePredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true))), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsBinaryImagePredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } void GenericDataFittingView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished")); this->m_FittingInProgress = false; this->UpdateGUIControls(); }; void GenericDataFittingView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void GenericDataFittingView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); }; void GenericDataFittingView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void GenericDataFittingView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void GenericDataFittingView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::ModelFitFunctorBase::Pointer GenericDataFittingView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(m_Controls.checkDebug->isChecked()); return fitFunctor.GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp index fd09cd5415..dcf1116823 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp @@ -1,905 +1,920 @@ /*============================================================================ 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. ============================================================================*/ // Blueberry #include #include #include // mitk #include // Qt #include #include #include #include #include "QmitkPlotWidget.h" #include "mitkNodePredicateFunction.h" #include "mitkScalarListLookupTableProperty.h" #include "mitkModelFitConstants.h" #include "mitkExtractTimeGrid.h" #include "mitkModelGenerator.h" #include "mitkModelFitException.h" #include "mitkModelFitParameterValueExtraction.h" #include "mitkTimeGridHelper.h" +#include "mitkModelFitResultRelationRule.h" #include "mitkModelFitPlotDataHelper.h" #include "ModelFitInspectorView.h" const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.gui.gt.fit.inspector"; const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 10; const std::string DEFAULT_X_AXIS = "Time [s]"; ModelFitInspectorView::ModelFitInspectorView() : m_renderWindowPart(nullptr), m_internalUpdateFlag(false), m_currentFit(nullptr), m_currentModelParameterizer(nullptr), m_currentModelProviderService(nullptr), m_currentSelectedTimeStep(0), m_currentSelectedNode(nullptr) { m_currentSelectedPosition.Fill(0.0); m_modelfitList.clear(); } ModelFitInspectorView::~ModelFitInspectorView() { } void ModelFitInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_renderWindowPart != renderWindowPart) { m_renderWindowPart = renderWindowPart; } this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { m_renderWindowPart = nullptr; this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void ModelFitInspectorView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_SelectionServiceConnector = std::make_unique(); m_SelectionServiceConnector->AddPostSelectionListener(this->GetSite()->GetWorkbenchWindow()->GetSelectionService()); m_Controls.inputNodeSelector->SetDataStorage(GetDataStorage()); m_Controls.inputNodeSelector->SetEmptyInfo(QString("Please select input data to be viewed.")); m_Controls.inputNodeSelector->SetInvalidInfo(QString("No input data is selected")); m_Controls.inputNodeSelector->SetPopUpTitel(QString("Choose 3D+t input data that should be viewed!")); m_Controls.inputNodeSelector->SetSelectionIsOptional(false); m_Controls.inputNodeSelector->SetSelectOnlyVisibleNodes(true); auto predicate = mitk::NodePredicateFunction::New([](const mitk::DataNode *node) { bool isModelFitNode = node->GetData() && node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str()).IsNotNull(); return isModelFitNode || (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); }); m_Controls.inputNodeSelector->SetNodePredicate(predicate); connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.inputNodeSelector, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ModelFitInspectorView::OnInputChanged); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); connect(m_Controls.cmbFit, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFitSelectionChanged(int))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.btnScaleToData, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedYChecked(bool))); connect(m_Controls.btnScaleToData, SIGNAL(clicked()), this, SLOT(OnScaleToDataYClicked())); connect(m_Controls.sbFixMax, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.sbFixMin, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.btnScaleToData_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedXChecked(bool))); connect(m_Controls.btnScaleToData_x, SIGNAL(clicked()), this, SLOT(OnScaleToDataXClicked())); connect(m_Controls.sbFixMax_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.sbFixMin_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.btnFullPlot, SIGNAL(clicked(bool)), this, SLOT(OnFullPlotClicked(bool))); this->EnsureBookmarkPointSet(); m_Controls.inspectionPositionWidget->SetPositionBookmarkNode(m_PositionBookmarksNode.Lock()); connect(m_Controls.inspectionPositionWidget, SIGNAL(PositionBookmarksChanged()), this, SLOT(OnPositionBookmarksChanged())); // For some reason this needs to be called to set the plot widget's minimum width to an // acceptable level (since Qwt 6). // Otherwise it tries to keep both axes equal in length, resulting in a minimum width of // 400-500px which is way too much. m_Controls.widgetPlot->GetPlot()->updateAxes(); m_Controls.cmbFit->clear(); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::SetFocus() { } void ModelFitInspectorView::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_currentSelectedNode) { QmitkSingleNodeSelectionWidget::NodeList emptylist; this->m_Controls.inputNodeSelector->SetCurrentSelection(emptylist); } } void ModelFitInspectorView::OnScaleFixedYChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::yLeft, !checked); if (checked) { OnScaleToDataYClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleFixedXChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::xBottom, !checked); if (checked) { OnScaleToDataXClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleToDataYClicked() { auto minmax = this->m_PlotCurves.GetYMinMax(); auto min = minmax.first - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::abs(minmax.second) * 0.01; m_Controls.sbFixMin->setValue(min); m_Controls.sbFixMax->setValue(max); }; void ModelFitInspectorView::OnScaleToDataXClicked() { auto minmax = this->m_PlotCurves.GetXMinMax(); auto min = minmax.first - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::abs(minmax.second) * 0.01; m_Controls.sbFixMin_x->setValue(min); m_Controls.sbFixMax_x->setValue(max); }; void ModelFitInspectorView::OnFixedScalingYChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::yLeft, m_Controls.sbFixMin->value(), m_Controls.sbFixMax->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFixedScalingXChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::xBottom, m_Controls.sbFixMin_x->value(), m_Controls.sbFixMax_x->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFullPlotClicked(bool checked) { m_Controls.tabWidget->setVisible(!checked); }; int ModelFitInspectorView::ActualizeFitSelectionWidget() { - mitk::NodeUIDType selectedFitUD = ""; - bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( - mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); + mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; + bool isModelFitNode = false; + if (this->m_Controls.inputNodeSelector->GetSelectedNode().IsNotNull()) + { + isModelFitNode = this->m_Controls.inputNodeSelector->GetSelectedNode()->GetData()->GetPropertyList()->GetStringProperty( + mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); + } mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::modelFit::NodeUIDSetType fitUIDs = mitk::modelFit::GetFitUIDsOfNode( this->m_currentSelectedNode, storage); this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); for (const auto & fitUID : fitUIDs) { mitk::modelFit::ModelFitInfo::ConstPointer info = mitk::modelFit::CreateFitInfoFromNode(fitUID, storage).GetPointer(); if (info.IsNotNull()) { this->m_modelfitList.insert(std::make_pair(info->uid, info)); std::ostringstream nameStrm; if (info->fitName.empty()) { nameStrm << info->uid; } else { nameStrm << info->fitName; } nameStrm << " (" << info->modelName << ")"; QVariant data(info->uid.c_str()); m_Controls.cmbFit->addItem(QString::fromStdString(nameStrm.str()), data); } else { MITK_ERROR << "Was not able to extract model fit information from storage. Node properties in storage may be invalid. Failed fit UID:" << fitUID; } } int cmbIndex = 0; if (m_modelfitList.empty()) { cmbIndex = -1; }; if (isModelFitNode) { //model was selected, thus select this one in combobox QVariant data(selectedFitUD.c_str()); cmbIndex = m_Controls.cmbFit->findData(data); if (cmbIndex == -1) { MITK_WARN << "Model fit Inspector in invalid state. Selected fit seems to be not avaible in plugin selection. Failed fit UID:" << selectedFitUD; } }; m_Controls.cmbFit->setCurrentIndex(cmbIndex); return cmbIndex; } void ModelFitInspectorView::OnInputChanged(const QList& nodes) { if (nodes.size() > 0) { if (nodes.front() != this->m_currentSelectedNode) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nodes.front(); - mitk::NodeUIDType selectedFitUD = ""; + mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); if (isModelFitNode) { - this->m_currentSelectedNode = this->GetParentNode(this->m_currentSelectedNode); + this->m_currentSelectedNode = this->GetInputNode(this->m_currentSelectedNode); + if (this->m_currentSelectedNode.IsNull()) + { + MITK_WARN << + "Model fit Inspector in invalid state. Input image for selected fit cannot be found in data storage. Failed fit UID:" + << selectedFitUD; + } } auto cmbIndex = ActualizeFitSelectionWidget(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); if (cmbIndex == -1) { //only raw 4D data selected. Just update plots for current position m_currentFit = nullptr; m_currentFitTime.Modified(); OnSliceChanged(); m_Controls.plotDataWidget->SetXName(DEFAULT_X_AXIS); } else { //refresh fit selection (and implicitly update plots) OnFitSelectionChanged(cmbIndex); } } } else { if (this->m_currentSelectedNode.IsNotNull()) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nullptr; this->m_currentFit = nullptr; this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); OnFitSelectionChanged(0); RefreshPlotData(); m_Controls.plotDataWidget->SetPlotData(&(this->m_PlotCurves)); m_Controls.fitParametersWidget->setFits(QmitkFitParameterModel::FitVectorType()); RenderPlot(); } } } mitk::DataNode::ConstPointer -ModelFitInspectorView::GetParentNode(mitk::DataNode::ConstPointer node) +ModelFitInspectorView::GetInputNode(mitk::DataNode::ConstPointer node) { if (node.IsNotNull()) { + std::string selectedFitUD = ""; + + auto rule = mitk::ModelFitResultRelationRule::New(); + auto predicate = rule->GetDestinationsDetector(node); mitk::DataStorage::SetOfObjects::ConstPointer parentNodeList = - GetDataStorage()->GetSources(node); + GetDataStorage()->GetSubset(predicate); if (parentNodeList->size() > 0) { return parentNodeList->front().GetPointer(); } } return mitk::DataNode::ConstPointer(); } void ModelFitInspectorView::ValidateAndSetCurrentPosition() { mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); unsigned int currentSelectedTimestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()-> GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimestep || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimestep; m_currentPositionTime.Modified(); m_validSelectedPosition = false; - mitk::Image::Pointer inputImage = this->GetCurrentInputImage(); + auto inputImage = this->GetCurrentInputImage(); if (inputImage.IsNull()) { return; } - mitk::BaseGeometry::Pointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep( - m_currentSelectedTimeStep); + mitk::BaseGeometry::ConstPointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep( + m_currentSelectedTimeStep).GetPointer(); // check for invalid time step if (geometry.IsNull()) { geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); } if (geometry.IsNull()) { return; } m_validSelectedPosition = geometry->IsInside(m_currentSelectedPosition); } } -mitk::Image::Pointer ModelFitInspectorView::GetCurrentInputImage() const +mitk::Image::ConstPointer ModelFitInspectorView::GetCurrentInputImage() const { - mitk::Image::Pointer result = nullptr; + mitk::Image::ConstPointer result = nullptr; if (this->m_currentFit.IsNotNull()) { result = m_currentFit->inputImage; } else if (this->m_currentSelectedNode.IsNotNull()) { result = dynamic_cast(this->m_currentSelectedNode->GetData()); if (result.IsNotNull() && result->GetTimeSteps() <= 1) { //if the image is not dynamic, we can't use it. result = nullptr; } } return result; }; const mitk::ModelBase::TimeGridType ModelFitInspectorView::GetCurrentTimeGrid() const { if (m_currentModelProviderService && m_currentFit.IsNotNull()) { return m_currentModelProviderService->GetVariableGrid(m_currentFit); } else { //fall back if there is no model provider we assume to use the normal time grid. return ExtractTimeGrid(GetCurrentInputImage()); } }; void ModelFitInspectorView::OnSliceChanged() { ValidateAndSetCurrentPosition(); m_Controls.widgetPlot->setEnabled(m_validSelectedPosition); if (m_currentSelectedNode.IsNotNull()) { m_Controls.inspectionPositionWidget->SetCurrentPosition(m_currentSelectedPosition); if (RefreshPlotData()) { RenderPlot(); m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } } void ModelFitInspectorView::OnPositionBookmarksChanged() { if (RefreshPlotData()) { RenderPlot(); m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } void ModelFitInspectorView::OnFitSelectionChanged(int index) { if (!m_internalUpdateFlag) { MITK_DEBUG << "selected fit index: " << index; std::string uid = ""; if (m_Controls.cmbFit->count() > index) { uid = m_Controls.cmbFit->itemData(index).toString().toStdString(); } mitk::modelFit::ModelFitInfo::ConstPointer newFit = nullptr; ModelFitInfoListType::iterator finding = m_modelfitList.find(uid); if (finding != m_modelfitList.end()) { newFit = finding->second; } if (m_currentFit != newFit) { m_currentModelParameterizer = nullptr; m_currentModelProviderService = nullptr; if (newFit.IsNotNull()) { m_currentModelParameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*newFit); m_currentModelProviderService = mitk::ModelGenerator::GetProviderService(newFit->functionClassID); } m_currentFit = newFit; m_currentFitTime.Modified(); auto name = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { name += " [" + m_currentFit->xAxisUnit + "]"; } m_Controls.plotDataWidget->SetXName(name); OnSliceChanged(); } } } mitk::PlotDataCurveCollection::Pointer ModelFitInspectorView::RefreshPlotDataCurveCollection(const mitk::Point3D& position, const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer) { mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New(); //sample curve if (input) { result->InsertElement(mitk::MODEL_FIT_PLOT_SAMPLE_NAME(), GenerateImageSamplePlotData(position, input, timeGrid)); } //model signal curve if (fitInfo) { // Interpolate time grid (x values) so the curve looks smooth const mitk::ModelBase::TimeGridType interpolatedTimeGrid = mitk::GenerateSupersampledTimeGrid(timeGrid, INTERPOLATION_STEPS); auto hires_curve = mitk::GenerateModelSignalPlotData(position, fitInfo, interpolatedTimeGrid, parameterizer); result->InsertElement(mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME(), hires_curve); auto curve = mitk::GenerateModelSignalPlotData(position, fitInfo, timeGrid, parameterizer); result->InsertElement(mitk::MODEL_FIT_PLOT_SIGNAL_NAME(), curve); } return result; }; bool ModelFitInspectorView::RefreshPlotData() { bool changed = false; if (m_currentSelectedNode.IsNull()) { this->m_PlotCurves = mitk::ModelFitPlotData(); changed = m_selectedNodeTime > m_lastRefreshTime; m_lastRefreshTime.Modified(); } else { assert(GetRenderWindowPart() != NULL); const mitk::Image* input = GetCurrentInputImage(); const mitk::ModelBase::TimeGridType timeGrid = GetCurrentTimeGrid(); if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime) { if (m_validSelectedPosition) { m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition,input,m_currentFit, timeGrid, m_currentModelParameterizer); } else { m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New(); } changed = true; } auto bookmarks = m_PositionBookmarks.Lock(); if (bookmarks.IsNotNull()) { if (m_currentFitTime > m_lastRefreshTime || bookmarks->GetMTime() > m_lastRefreshTime) { m_PlotCurves.positionalPlots.clear(); auto endIter = bookmarks->End(); for (auto iter = bookmarks->Begin(); iter != endIter; iter++) { auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer); m_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection)); } changed = true; } } else { m_PlotCurves.positionalPlots.clear(); } // input data curve if (m_currentFitTime > m_lastRefreshTime) { m_PlotCurves.staticPlots->clear(); if (m_currentFit.IsNotNull()) { m_PlotCurves.staticPlots = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid); } changed = true; } m_lastRefreshTime.Modified(); } return changed; } void ModelFitInspectorView::RenderFitInfo() { assert(m_renderWindowPart != nullptr); // configure fit information if (m_currentFit.IsNull()) { m_Controls.lFitType->setText(""); m_Controls.lFitUID->setText(""); m_Controls.lModelName->setText(""); m_Controls.lModelType->setText(""); } else { m_Controls.lFitType->setText(QString::fromStdString(m_currentFit->fitType)); m_Controls.lFitUID->setText(QString::fromStdString(m_currentFit->uid)); m_Controls.lModelName->setText(QString::fromStdString(m_currentFit->modelName)); m_Controls.lModelType->setText(QString::fromStdString(m_currentFit->modelType)); } // print results std::stringstream infoOutput; m_Controls.fitParametersWidget->setVisible(false); m_Controls.groupSettings->setVisible(false); if (m_currentFit.IsNull()) { infoOutput << "No fit selected. Only raw image data is plotted."; } else if (!m_validSelectedPosition) { infoOutput << "Current position is outside of the input image of the selected fit.\nInspector is deactivated."; } else { m_Controls.fitParametersWidget->setVisible(true); m_Controls.fitParametersWidget->setFits({ m_currentFit }); m_Controls.fitParametersWidget->setPositionBookmarks(m_PositionBookmarks.Lock()); m_Controls.fitParametersWidget->setCurrentPosition(m_currentSelectedPosition); } // configure data table m_Controls.tableInputData->clearContents(); if (m_currentFit.IsNull()) { infoOutput << "No fit selected. Only raw image data is plotted."; } else { m_Controls.groupSettings->setVisible(true); m_Controls.tableInputData->setRowCount(m_PlotCurves.staticPlots->size()); unsigned int rowIndex = 0; for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); pos != m_PlotCurves.staticPlots->end(); ++pos, ++rowIndex) { QColor dataColor; if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); } else { //Use HSV schema of QColor to calculate a different color depending on the //number of already existing free iso lines. dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 255); } QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first)); m_Controls.tableInputData->setItem(rowIndex, 0, newItem); newItem = new QTableWidgetItem(); newItem->setBackgroundColor(dataColor); m_Controls.tableInputData->setItem(rowIndex, 1, newItem); } } m_Controls.lInfo->setText(QString::fromStdString(infoOutput.str())); } void ModelFitInspectorView::RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString) { auto sampleCurve = mitk::ModelFitPlotData::GetSamplePlot(curveCollection); if (sampleCurve) { std::string name = mitk::MODEL_FIT_PLOT_SAMPLE_NAME() + posString; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, sampleCurve->GetValues()); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Diamond, sampleColor, sampleColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } //draw model curve auto signalCurve = mitk::ModelFitPlotData::GetInterpolatedSignalPlot(curveCollection); if (signalCurve) { std::string name = mitk::MODEL_FIT_PLOT_SIGNAL_NAME() + posString; QPen pen; pen.setColor(signalColor); pen.setWidth(2); unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, signalCurve->GetValues()); m_Controls.widgetPlot->SetCurvePen(curveId, pen); // Manually set the legend attribute to use the symbol as the legend icon and alter its // size. Otherwise it would revert to default which is drawing a square which is the color // of the curve's pen, so in this case none which defaults to black. // Unfortunately, QmitkPlotWidget offers no way to set the legend attribute and icon size so // this looks a bit hacky. QwtPlotCurve* fitCurve = dynamic_cast(m_Controls.widgetPlot->GetPlot()-> itemList(QwtPlotItem::Rtti_PlotCurve).back()); fitCurve->setLegendAttribute(QwtPlotCurve::LegendShowLine); } } void ModelFitInspectorView::RenderPlot() { m_Controls.widgetPlot->Clear(); std::string xAxis = DEFAULT_X_AXIS; std::string yAxis = "Intensity"; std::string plotTitle = "Raw data plot: no data"; if (m_currentSelectedNode.IsNotNull()) { plotTitle = "Raw data plot: " + m_currentSelectedNode->GetName(); } if (m_currentFit.IsNotNull()) { plotTitle = m_currentFit->modelName.c_str(); xAxis = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { xAxis += " [" + m_currentFit->xAxisUnit + "]"; } yAxis = m_currentFit->yAxisName; if (!m_currentFit->yAxisUnit.empty()) { yAxis += " [" + m_currentFit->yAxisUnit + "]"; } } m_Controls.widgetPlot->SetAxisTitle(QwtPlot::xBottom, xAxis.c_str()); m_Controls.widgetPlot->SetAxisTitle(QwtPlot::yLeft, yAxis.c_str()); m_Controls.widgetPlot->SetPlotTitle(plotTitle.c_str()); // Draw static curves unsigned int colorIndex = 0; for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); pos != m_PlotCurves.staticPlots->end(); ++pos) { QColor dataColor; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(pos->first.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, pos->second->GetValues()); if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); QPen pen; pen.setColor(dataColor); pen.setStyle(Qt::SolidLine); m_Controls.widgetPlot->SetCurvePen(curveId, pen); } else { //Use HSV schema of QColor to calculate a different color depending on the //number of already existing curves. dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); } // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Triangle, dataColor, dataColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } // Draw positional curves for (const auto& posIter : this->m_PlotCurves.positionalPlots) { QColor dataColor; dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); this->RenderPlotCurve(posIter.second.second, dataColor, dataColor, " @ "+mitk::ModelFitPlotData::GetPositionalCollectionName(posIter)); } // Draw current pos curve this->RenderPlotCurve(m_PlotCurves.currentPositionPlots, QColor(Qt::red), QColor(Qt::black), ""); QwtLegend* legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); m_Controls.widgetPlot->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.widgetPlot->Replot(); } void ModelFitInspectorView::EnsureBookmarkPointSet() { if (m_PositionBookmarks.IsExpired() || m_PositionBookmarksNode.IsExpired()) { const char* nodeName = "org.mitk.gui.qt.fit.inspector.positions"; mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName); if (!node) { node = mitk::DataNode::New(); node->SetName(nodeName); node->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(node); } m_PositionBookmarksNode = node; mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { pointSet = mitk::PointSet::New(); node->SetData(pointSet); } m_PositionBookmarks = pointSet; } } diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h index 864bbc914c..45287d9aaf 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h @@ -1,201 +1,202 @@ /*============================================================================ 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 ModelFitInspectorView_h #define ModelFitInspectorView_h // Blueberry //#include #include // mitk #include #include #include "QmitkSliceNavigationListener.h" #include "mitkModelFitStaticParameterMap.h" #include "mitkModelParameterizerBase.h" #include "mitkModelFitInfo.h" #include "mitkIModelFitProvider.h" #include "mitkModelFitPlotDataHelper.h" #include "QmitkSelectionServiceConnector.h" #include "QmitkFitParameterModel.h" // Qt #include "ui_ModelFitInspectorViewControls.h" /** * @brief View class defining the UI part of the ModelFitInspector plug-in. */ class ModelFitInspectorView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: ModelFitInspectorView(); ~ModelFitInspectorView() override; static const std::string VIEW_ID; protected slots: void OnSliceChanged(); /** * @brief Triggered when the selection of the "Modelfit" combo box changes. * Sets the selected fit as the current one. * @param index The index (in the combo box) of the selected item. */ void OnFitSelectionChanged(int index); void OnInputChanged(const QList& nodes); void OnPositionBookmarksChanged(); /** Triggered when the selection of "fixed" y axis scaling changes*/ void OnScaleFixedYChecked(bool checked); void OnScaleToDataYClicked(); void OnFixedScalingYChanged(double value); /** Triggered when the selection of "fixed" x axis scaling changes*/ void OnScaleFixedXChecked(bool checked); void OnScaleToDataXClicked(); void OnFixedScalingXChanged(double value); void OnFullPlotClicked(bool checked); protected: void CreateQtPartControl(QWidget* parent) override; void SetFocus() override; void NodeRemoved(const mitk::DataNode* node) override; /** Helper that actualizes the fit selection widget and returns the index of the currently selected * fit.*/ int ActualizeFitSelectionWidget(); void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; /** * @brief Calculates the curve data using the current fit's model and parameterizer. * @param position Indicating the point in the input image for which the model curve should be calculated. * @return A vector of points for the curve data. */ QmitkPlotWidget::XYDataVector CalcCurveFromModel(const mitk::Point3D& position); /** * @brief Calculates the curve data using the current fit's function string. * @param timeGrid The time grid containing the (interpolated) x values for the curve data. * @return A vector of points for the curve data. */ QmitkPlotWidget::XYDataVector CalcCurveFromFunction(const mitk::Point3D& position, const mitk::ModelBase::TimeGridType& timeGrid); /** - * @brief Returns the parent node of the given node if it exists. - * @param node The node whose parent node should be returned. - * @return The parent node of the given node or NULL if it doesn't exist. + * @brief Returns the input node that was used to generate the passed model fit node if it exists. + * @param node Node that is a model fit result. + * @return The input node that was used to generate the passed node. If the input node cannot + * be found in the storage or the passed node is not a model fit result node, a nullptr will be returned. */ - mitk::DataNode::ConstPointer GetParentNode(mitk::DataNode::ConstPointer node); + mitk::DataNode::ConstPointer GetInputNode(mitk::DataNode::ConstPointer node); /** Sets m_currentSelectedPosition to the current selection and validates if this position is valid * for the input image of the currently selected fit. If it is valid, m_validSelectedPosition is set to true. * If the fit, his input image or geometry is not specified, it will also handled as invalid.*/ void ValidateAndSetCurrentPosition(); /** Returns the current input image. If a current fit is set it will be its input image. * Otherwise it will be the image stored in the currently selected node. If the node is not set, contains no image * or the image is not 4D, NULL will be returned.*/ - mitk::Image::Pointer GetCurrentInputImage() const; + mitk::Image::ConstPointer GetCurrentInputImage() const; /** Returns the variable/time grid of the GetCurrentInputImage(). If a model fit is selected its provider will be used to get the correct grid, otherwise just a simple time grid will be extracted.*/ const mitk::ModelBase::TimeGridType GetCurrentTimeGrid() const; Ui::ModelFitInspectorViewControls m_Controls; mitk::IRenderWindowPart* m_renderWindowPart; /** @brief Is a visualization currently running? */ bool m_internalUpdateFlag; /** @brief List of modelfits currently in the data manager */ typedef std::map ModelFitInfoListType; ModelFitInfoListType m_modelfitList; /** @brief The currently selected modelfit */ mitk::modelFit::ModelFitInfo::ConstPointer m_currentFit; /** @brief Pointer to the instance of the model parameterizer for the current fit */ mitk::ModelParameterizerBase::Pointer m_currentModelParameterizer; mitk::IModelFitProvider* m_currentModelProviderService; /** @brief currently valid selected position in the inspector*/ mitk::Point3D m_currentSelectedPosition; /** @brief indicates if the currently selected position is valid for the currently selected fit. * This it is within the input image */ bool m_validSelectedPosition; /** @brief currently selected time step of the selected node for the visualization logic*/ unsigned int m_currentSelectedTimeStep; /** @brief currently selected node for the visualization logic*/ mitk::DataNode::ConstPointer m_currentSelectedNode; mitk::WeakPointer m_PositionBookmarksNode; mitk::WeakPointer m_PositionBookmarks; /** @brief Number of interpolation steps between two x values */ static const unsigned int INTERPOLATION_STEPS; /*************************************/ /* Members for visualizing the model */ itk::TimeStamp m_selectedNodeTime; itk::TimeStamp m_currentFitTime; itk::TimeStamp m_currentPositionTime; itk::TimeStamp m_lastRefreshTime; mitk::ModelFitPlotData m_PlotCurves; std::unique_ptr m_SelectionServiceConnector; QmitkFitParameterModel* m_FitParameterModel; QmitkSliceNavigationListener m_SliceChangeListener; /** Check and updates the plot data if needed. * @return indicates if something was refreshed (true)*/ bool RefreshPlotData(); void RenderPlot(); void RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString); void RenderFitInfo(); void EnsureBookmarkPointSet(); static mitk::PlotDataCurveCollection::Pointer RefreshPlotDataCurveCollection(const mitk::Point3D& position, const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer); }; #endif // ModelFitInspectorView_h diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp index 17c6f05ca9..586c31959a 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp @@ -1,1454 +1,1448 @@ /*============================================================================ 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 "MRPerfusionView.h" #include "boost/tokenizer.hpp" #include "boost/math/constants/constants.hpp" #include #include "mitkWorkbenchUtil.h" #include "mitkAterialInputFunctionGenerator.h" #include "mitkConcentrationCurveGenerator.h" #include #include #include #include "mitkThreeStepLinearModelFactory.h" #include "mitkThreeStepLinearModelParameterizer.h" #include "mitkTwoStepLinearModelFactory.h" #include "mitkTwoStepLinearModelParameterizer.h" #include #include #include #include #include "mitkTwoCompartmentExchangeModelFactory.h" #include "mitkTwoCompartmentExchangeModelParameterizer.h" #include "mitkNumericTwoCompartmentExchangeModelFactory.h" #include "mitkNumericTwoCompartmentExchangeModelParameterizer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include const std::string MRPerfusionView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.mri"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); stepStream.imbue(std::locale("C")); double value = 0.0; if (!(stepStream >> value) || !(stepStream.eof())) { mitkThrow() << "Cannot convert string to double. String: " << data; } return value; } void MRPerfusionView::SetFocus() { m_Controls.btnModelling->setFocus(); } void MRPerfusionView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); this->InitModelComboBox(); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); //AIF setting m_Controls.groupAIF->hide(); m_Controls.btnAIFFile->setEnabled(false); m_Controls.btnAIFFile->setEnabled(false); m_Controls.radioAIFImage->setChecked(true); m_Controls.comboAIFMask->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFMask->SetPredicate(m_IsMaskPredicate); m_Controls.comboAIFMask->setVisible(true); m_Controls.comboAIFMask->setEnabled(true); m_Controls.comboAIFImage->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFImage->SetPredicate(m_IsNoMaskImagePredicate); m_Controls.comboAIFImage->setEnabled(false); m_Controls.checkDedicatedAIFImage->setEnabled(true); m_Controls.HCLSpinBox->setValue(mitk::AterialInputFunctionGenerator::DEFAULT_HEMATOCRIT_LEVEL); m_Controls.spinBox_baselineEndTimeStep->setMinimum(0); m_Controls.spinBox_baselineStartTimeStep->setMinimum(0); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setVisible(bool))); connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile())); //Brix setting m_Controls.groupDescBrix->hide(); connect(m_Controls.injectiontime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Num2CX setting m_Controls.groupNum2CXM->hide(); connect(m_Controls.odeStepSize, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage()); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); //Concentration m_Controls.groupConcentration->hide(); m_Controls.groupBoxEnhancement->hide(); m_Controls.groupBoxTurboFlash->hide(); m_Controls.radioButtonNoConversion->setChecked(true); m_Controls.groupBox_T1MapviaVFA->hide(); m_Controls.spinBox_baselineStartTimeStep->setValue(0); m_Controls.spinBox_baselineEndTimeStep->setValue(0); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, SLOT(setVisible(bool))); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxationtime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.recoverytime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxivity, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.spinBox_baselineStartTimeStep, SIGNAL(valueChanged(int)), this, SLOT(UpdateGUIControls())); connect(m_Controls.spinBox_baselineEndTimeStep, SIGNAL(valueChanged(int)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_T1MapviaVFA, SLOT(setVisible(bool))); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.FlipangleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.RelaxivitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.TRSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); m_Controls.ComboPDWImage->SetDataStorage(this->GetDataStorage()); m_Controls.ComboPDWImage->SetPredicate(m_IsNoMaskImagePredicate); m_Controls.ComboPDWImage->setEnabled(false); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.ComboPDWImage, SLOT(setEnabled(bool))); UpdateGUIControls(); } bool MRPerfusionView::IsTurboFlashSequenceFlag() const { return this->m_Controls.radioButtonTurboFlash->isChecked(); }; void MRPerfusionView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isSLFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupAIF->setVisible(isToftsFactory || is2CXMFactory); m_Controls.groupDescBrix->setVisible(isDescBrixFactory); m_Controls.groupNum2CXM->setVisible(isNum2CXMFactory); m_Controls.groupConcentration->setVisible(isToftsFactory || is2CXMFactory || isSLFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupAIF->setEnabled(!m_FittingInProgress); m_Controls.groupDescBrix->setEnabled(!m_FittingInProgress); m_Controls.groupNum2CXM->setEnabled(!m_FittingInProgress); m_Controls.groupConcentration->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); m_Controls.spinBox_baselineStartTimeStep->setEnabled(m_Controls.radioButtonTurboFlash->isChecked() || m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked()); m_Controls.spinBox_baselineEndTimeStep->setEnabled(m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked() || m_Controls.radioButtonTurboFlash->isChecked()); } void MRPerfusionView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } if (m_selectedModelFactory) { this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(), m_selectedModelFactory->GetDefaultInitialParameterization()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, this->m_selectedModelFactory->GetParameterNames()); } UpdateGUIControls(); } std::string MRPerfusionView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string MRPerfusionView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void MRPerfusionView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { m_HasGeneratedNewInput = false; m_HasGeneratedNewInputAIF = false; mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is3LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isExtToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isStanToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isDescBrixFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateDescriptiveBrixModel_PixelBased(fitSession, generator); } else { GenerateDescriptiveBrixModel_ROIBased(fitSession, generator); } } else if (is2LinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateLinearModelFit_PixelBased(fitSession, generator); } else { GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (is3LinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateLinearModelFit_PixelBased(fitSession, generator); } else { GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (isStanToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isExtToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (is2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isNum2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; UpdateGUIControls(); DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void MRPerfusionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& selectedNodes) { m_selectedMaskNode = nullptr; m_selectedMask = nullptr; m_Controls.masklabel->setText("No (valid) mask selected."); m_Controls.timeserieslabel->setText("No (valid) series selected."); QList nodes = selectedNodes; if (nodes.size() > 0 && this->m_IsNoMaskImagePredicate->CheckNode(nodes.front())) { this->m_selectedNode = nodes.front(); auto selectedImage = dynamic_cast(this->m_selectedNode->GetData()); m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); if (selectedImage != this->m_selectedImage) { if (selectedImage) { this->m_Controls.initialValuesManager->setReferenceImageGeometry(selectedImage->GetGeometry()); } else { this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } } this->m_selectedImage = selectedImage; nodes.pop_front(); } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } if (nodes.size() > 0 && this->m_IsMaskPredicate->CheckNode(nodes.front())) { this->m_selectedMaskNode = nodes.front(); this->m_selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str()); } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } if (this->m_selectedImage.IsNotNull()) { m_Controls.spinBox_baselineStartTimeStep->setMaximum((this->m_selectedImage->GetDimension(3))-1); m_Controls.spinBox_baselineEndTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); } UpdateGUIControls(); } bool MRPerfusionView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is3LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr|| dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isDescBrixFactory) { //if all static parameters for this model are set, exit with true, Otherwise exit with false ok = m_Controls.injectiontime->value() > 0; } else if (is3LinearFactory || is2LinearFactory) { if (this->m_Controls.radioButtonTurboFlash->isChecked() ) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked() ) { ok = ok && (m_Controls.factorSpinBox->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked() ) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.ComboPDWImage->GetSelectedNode().IsNotNull()); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = true; } else { ok = false; } } else if (isToftsFactory || is2CXMFactory || isNum2CXMFactory) { if (this->m_Controls.radioAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFMask->GetSelectedNode().IsNotNull(); if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFImage->GetSelectedNode().IsNotNull(); } } else if (this->m_Controls.radioAIFFile->isChecked()) { ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0); } else { ok = false; } if (this->m_Controls.radioButtonTurboFlash->isChecked() ) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked() ) { ok = ok && (m_Controls.factorSpinBox->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked() ) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.ComboPDWImage->GetSelectedNode().IsNotNull()); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = ok && true; } else { ok = false; } if (isNum2CXMFactory) { ok = ok && (this->m_Controls.odeStepSize->value() > 0); } } //add other models as else if and check wether all needed static parameters are set else { ok = false; } if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues()) { std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source."; MITK_ERROR << warning; m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString("")); ok = false; }; } else { ok = false; } return ok; } bool MRPerfusionView::CheckBaselineSelectionSettings() const { return m_Controls.spinBox_baselineStartTimeStep->value() <= m_Controls.spinBox_baselineEndTimeStep->value(); } void MRPerfusionView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate(); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } } void MRPerfusionView::GenerateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelParameterizer::New(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_selectedImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage; mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage); modelParameterizer->SetBaseImage(baseImage); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); - roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + roiUID = m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } void MRPerfusionView::GenerateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); modelParameterizer->SetBaseValue(roiSignal[0]); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void MRPerfusionView::GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); - roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + roiUID = this->m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } template void MRPerfusionView::GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void MRPerfusionView::GenerateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); mitk::NumericTwoCompartmentExchangeModelParameterizer* numTCXParametrizer = dynamic_cast (modelParameterizer.GetPointer()); if (numTCXParametrizer) { numTCXParametrizer->SetODEINTStepSize(this->m_Controls.odeStepSize->value()); } //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); - roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + roiUID = this->m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(this->m_inputImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } template void MRPerfusionView::GenerateAIFbasedModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); mitk::NumericTwoCompartmentExchangeModelParameterizer* numTCXParametrizer = dynamic_cast (modelParameterizer.GetPointer()); if (numTCXParametrizer) { numTCXParametrizer->SetODEINTStepSize(this->m_Controls.odeStepSize->value()); } //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(this->m_inputImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(this->m_inputImage)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); infoSignal.clear(); for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } void MRPerfusionView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { this->m_Controls.infoBox->append(QString("" + QString("Fitting Data Set . . .") + QString (""))); ///////////////////////// //create job and put it into the thread pool mitk::modelFit::ModelFitResultNodeVectorType additionalNodes; if (m_HasGeneratedNewInput) { additionalNodes.push_back(m_inputNode); } if (m_HasGeneratedNewInputAIF) { additionalNodes.push_back(m_inputAIFNode); } ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode, additionalNodes); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } MRPerfusionView::MRPerfusionView() : m_FittingInProgress(false), m_HasGeneratedNewInput(false), m_HasGeneratedNewInputAIF(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoStepLinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ThreeStepLinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::StandardToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ExtendedToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::NumericTwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } void MRPerfusionView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished.")); this->m_FittingInProgress = false; this->UpdateGUIControls(); }; void MRPerfusionView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void MRPerfusionView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); //this stores the concentration image and AIF concentration image, if generated for this fit in the storage. //if not generated for this fit, relevant nodes are empty. mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), pJob->GetAdditionalRelevantNodes(), pJob->GetParentNode()); }; void MRPerfusionView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void MRPerfusionView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void MRPerfusionView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::DataNode::Pointer MRPerfusionView::GenerateConcentrationNode(mitk::Image* image, const std::string& nodeName) const { if (!image) { mitkThrow() << "Cannot generate concentration node. Passed image is null. parameter name: "; } mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetData(image); result->SetName(nodeName); result->SetVisibility(true); - mitk::EnsureModelFitUID(result); - return result; }; mitk::Image::Pointer MRPerfusionView::ConvertConcentrationImage(bool AIFMode) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); if (m_Controls.checkDedicatedAIFImage->isChecked() && AIFMode) { concentrationGen->SetDynamicImage(this->m_selectedAIFImage); } else { concentrationGen->SetDynamicImage(this->m_selectedImage); } concentrationGen->SetisTurboFlashSequence(IsTurboFlashSequenceFlag()); concentrationGen->SetAbsoluteSignalEnhancement(m_Controls.radioButton_absoluteEnhancement->isChecked()); concentrationGen->SetRelativeSignalEnhancement(m_Controls.radioButton_relativeEnchancement->isChecked()); concentrationGen->SetUsingT1Map(m_Controls.radioButtonUsingT1viaVFA->isChecked()); if (IsTurboFlashSequenceFlag()) { if (AIFMode) { concentrationGen->SetRecoveryTime(m_Controls.AifRecoverytime->value()); } else { concentrationGen->SetRecoveryTime(m_Controls.recoverytime->value()); } concentrationGen->SetRelaxationTime(m_Controls.relaxationtime->value()); concentrationGen->SetRelaxivity(m_Controls.relaxivity->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) { concentrationGen->SetRecoveryTime(m_Controls.TRSpinBox->value()); concentrationGen->SetRelaxivity(m_Controls.RelaxivitySpinBox->value()); concentrationGen->SetT10Image(dynamic_cast(m_Controls.ComboPDWImage->GetSelectedNode()->GetData())); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); //Convert Flipangle from degree to radiant double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); concentrationGen->SetFlipAngle(alpha); } else { concentrationGen->SetFactor(m_Controls.factorSpinBox->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } void MRPerfusionView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (this->m_Controls.radioAIFFile->isChecked()) { aif.clear(); aifTimeGrid.clear(); aif.SetSize(AIFinputFunction.size()); aifTimeGrid.SetSize(AIFinputGrid.size()); aif.fill(0.0); aifTimeGrid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for (std::vector::const_iterator pos = AIFinputFunction.begin(); pos != AIFinputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = aifTimeGrid.begin(); for (std::vector::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } } else if (this->m_Controls.radioAIFImage->isChecked()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value()); //mask settings this->m_selectedAIFMaskNode = m_Controls.comboAIFMask->GetSelectedNode(); this->m_selectedAIFMask = dynamic_cast(this->m_selectedAIFMaskNode->GetData()); if (this->m_selectedAIFMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: " << m_selectedAIFMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedAIFMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput(); } if (this->m_selectedAIFMask.IsNotNull()) { aifGenerator->SetMask(this->m_selectedAIFMask); } //image settings if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { this->m_selectedAIFImageNode = m_Controls.comboAIFImage->GetSelectedNode(); this->m_selectedAIFImage = dynamic_cast(this->m_selectedAIFImageNode->GetData()); } else { this->m_selectedAIFImageNode = m_selectedNode; this->m_selectedAIFImage = m_selectedImage; } this->PrepareAIFConcentrationImage(); aifGenerator->SetDynamicImage(this->m_inputAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. View is in a invalide state. No AIF mode selected."; } } void MRPerfusionView::LoadAIFfromFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.aifFilePath->setText(fileName); std::string m_aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::ifstream in1(m_aifFilePath.c_str()); if (!in1.is_open()) { this->m_Controls.infoBox->append(QString("Could not open AIF File!")); } std::vector< std::string > vec1; std::string line1; while (getline(in1, line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(), tok.end()); this->AIFinputGrid.push_back(convertToDouble(vec1[0])); this->AIFinputFunction.push_back(convertToDouble(vec1[1])); } } void MRPerfusionView::PrepareConcentrationImage() { mitk::Image::Pointer concentrationImage = this->m_selectedImage; mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; m_HasGeneratedNewInput = false; if (!this->m_Controls.radioButtonNoConversion->isChecked()) { concentrationImage = this->ConvertConcentrationImage(false); concentrationNode = GenerateConcentrationNode(concentrationImage, "Concentration"); m_HasGeneratedNewInput = true; } m_inputImage = concentrationImage; m_inputNode = concentrationNode; - - mitk::EnsureModelFitUID(concentrationNode); } void MRPerfusionView::PrepareAIFConcentrationImage() { mitk::Image::Pointer concentrationImage = this->m_selectedImage; mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; m_HasGeneratedNewInputAIF = false; if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { concentrationImage = this->m_selectedAIFImage; concentrationNode = this->m_selectedAIFImageNode; } if (!this->m_Controls.radioButtonNoConversion->isChecked()) { if (!IsTurboFlashSequenceFlag() && !this->m_Controls.checkDedicatedAIFImage->isChecked()) { if (m_inputImage.IsNull()) { mitkThrow() << "Cannot get AIF concentration image. Invalid view state. Input image is not defined yet, but should be."; } //we can directly use the concentration input image/node (generated by GetConcentrationImage) also for the AIF concentrationImage = this->m_inputImage; concentrationNode = this->m_inputNode; } else { concentrationImage = this->ConvertConcentrationImage(true); concentrationNode = GenerateConcentrationNode(concentrationImage, "AIF Concentration"); m_HasGeneratedNewInputAIF = true; } } m_inputAIFImage = concentrationImage; m_inputAIFNode = concentrationNode; - - mitk::EnsureModelFitUID(concentrationNode); } mitk::ModelFitFunctorBase::Pointer MRPerfusionView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(m_Controls.checkDebug->isChecked()); return fitFunctor.GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp index ed2966912d..dc4a82d910 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp @@ -1,942 +1,946 @@ /*============================================================================ 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 "PETDynamicView.h" #include "mitkWorkbenchUtil.h" #include "mitkAterialInputFunctionGenerator.h" #include "mitkOneTissueCompartmentModelFactory.h" #include "mitkOneTissueCompartmentModelParameterizer.h" #include "mitkExtendedOneTissueCompartmentModelFactory.h" #include "mitkExtendedOneTissueCompartmentModelParameterizer.h" #include "mitkTwoTissueCompartmentFDGModelFactory.h" #include "mitkTwoTissueCompartmentFDGModelParameterizer.h" #include "mitkTwoTissueCompartmentModelFactory.h" #include "mitkTwoTissueCompartmentModelParameterizer.h" #include "mitkNumericTwoTissueCompartmentModelFactory.h" #include "mitkNumericTwoTissueCompartmentModelParameterizer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include #include const std::string PETDynamicView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.pet"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); double value = 0.0; stepStream >> value; return value; } void PETDynamicView::SetFocus() { m_Controls.btnModelling->setFocus(); } void PETDynamicView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); m_Controls.errorMessageLabel->hide(); this->InitModelComboBox(); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); //AIF setting m_Controls.groupAIF->hide(); m_Controls.btnAIFFile->setEnabled(false); m_Controls.btnAIFFile->setEnabled(false); m_Controls.radioAIFImage->setChecked(true); m_Controls.comboAIFMask->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFMask->SetPredicate(m_IsMaskPredicate); m_Controls.comboAIFMask->setVisible(true); m_Controls.comboAIFMask->setEnabled(true); m_Controls.comboAIFImage->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFImage->SetPredicate(m_IsNoMaskImagePredicate); m_Controls.comboAIFImage->setEnabled(false); m_Controls.checkDedicatedAIFImage->setEnabled(true); m_Controls.HCLSpinBox->setValue(0.0); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setVisible(bool))); connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage()); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); UpdateGUIControls(); } void PETDynamicView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool is1TCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isExt1TCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isFDGCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2TCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupAIF->setVisible(is1TCMFactory || isExt1TCMFactory || isFDGCMFactory || is2TCMFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupAIF->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); } //void PETDynamicView::OnModelSettingChanged() //{ // bool ok = m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings(); // m_Controls.btnModelling->setEnabled(ok); //} void PETDynamicView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } if (m_selectedModelFactory) { this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(), m_selectedModelFactory->GetDefaultInitialParameterization()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, this->m_selectedModelFactory->GetParameterNames()); } m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); UpdateGUIControls(); } std::string PETDynamicView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string PETDynamicView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void PETDynamicView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isextOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isFDGFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNumTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isOTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isextOTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isFDGFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isTTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isNumTTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void PETDynamicView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& selectedNodes) { m_selectedMaskNode = nullptr; m_selectedMask = nullptr; m_Controls.errorMessageLabel->setText(""); m_Controls.masklabel->setText("No (valid) mask selected."); m_Controls.timeserieslabel->setText("No (valid) series selected."); QList nodes = selectedNodes; if (nodes.size() > 0 && this->m_IsNoMaskImagePredicate->CheckNode(nodes.front())) { this->m_selectedNode = nodes.front(); auto selectedImage = dynamic_cast(this->m_selectedNode->GetData()); m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); if (selectedImage != this->m_selectedImage) { if (selectedImage) { this->m_Controls.initialValuesManager->setReferenceImageGeometry(selectedImage->GetGeometry()); } else { this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } } this->m_selectedImage = selectedImage; nodes.pop_front(); } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } if (nodes.size() > 0 && this->m_IsMaskPredicate->CheckNode(nodes.front())) { this->m_selectedMaskNode = nodes.front(); this->m_selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str()); } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } m_Controls.errorMessageLabel->show(); UpdateGUIControls(); } bool PETDynamicView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isextOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isFDGFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNumTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isOTCFactory || isextOTCFactory || isFDGFactory || isTTCFactory || isNumTTCFactory) { if (this->m_Controls.radioAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFMask->GetSelectedNode().IsNotNull(); if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFImage->GetSelectedNode().IsNotNull(); } } else if (this->m_Controls.radioAIFFile->isChecked()) { ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0); } else { ok = false; } } //add other models as else if and check wether all needed static parameters are set else { ok = false; } if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues()) { std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source."; MITK_ERROR << warning; m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString("")); ok = false; }; } else { ok = false; } return ok; } void PETDynamicView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate(); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } } template void PETDynamicView::GenerateModelFit_PixelBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::TimeGridType aifTimeGrid; GetAIF(aif, aifTimeGrid); //Model configuration (static parameters) can be done now modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); - roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + roiUID = m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(this->m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } template void PETDynamicView::GenerateModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); //Compute AIF mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); aifGenerator->SetDynamicImage(this->m_selectedImage); aifGenerator->SetMask(this->m_selectedAIFMask); mitk::AIFBasedModelBase::AterialInputFunctionType aif = aifGenerator->GetAterialInputFunction(); mitk::AIFBasedModelBase::TimeGridType aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); //Model configuration (static parameters) can be done now modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); - std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); + std::string roiUID = ""; + if (m_selectedMask.IsNotNull()) + { + roiUID = m_selectedMask->GetUID(); + } //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); infoSignal.clear(); for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } void PETDynamicView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { std::stringstream message; message << "" << "Fitting Data Set . . ." << ""; m_Controls.errorMessageLabel->setText(message.str().c_str()); m_Controls.errorMessageLabel->show(); ///////////////////////// //create job and put it into the thread pool ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } PETDynamicView::PETDynamicView() : m_FittingInProgress(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::OneTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ExtendedOneTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoTissueCompartmentFDGModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::NumericTwoTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } void PETDynamicView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished")); this->m_FittingInProgress = false; }; void PETDynamicView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void PETDynamicView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); m_Controls.errorMessageLabel->setText(""); m_Controls.errorMessageLabel->hide(); }; void PETDynamicView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void PETDynamicView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void PETDynamicView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::ModelFitFunctorBase::Pointer PETDynamicView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::SumOfSquaredDifferencesFitCostFunction::Pointer evaluation = mitk::SumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("sum_diff^2", evaluation); mitk::ChiSquareFitCostFunction::Pointer chi2 = mitk::ChiSquareFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); mitk::ReducedChiSquareFitCostFunction::Pointer redchi2 = mitk::ReducedChiSquareFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("redChi^2", redchi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); return fitFunctor.GetPointer(); } void PETDynamicView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (this->m_Controls.radioAIFFile->isChecked()) { aif.clear(); aifTimeGrid.clear(); aif.SetSize(AIFinputFunction.size()); aifTimeGrid.SetSize(AIFinputGrid.size()); aif.fill(0.0); aifTimeGrid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for (std::vector::const_iterator pos = AIFinputFunction.begin(); pos != AIFinputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = aifTimeGrid.begin(); for (std::vector::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } } else if (this->m_Controls.radioAIFImage->isChecked()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value()); //mask settings this->m_selectedAIFMaskNode = m_Controls.comboAIFMask->GetSelectedNode(); this->m_selectedAIFMask = dynamic_cast(this->m_selectedAIFMaskNode->GetData()); if (this->m_selectedAIFMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: " << m_selectedAIFMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedAIFMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput(); } if (this->m_selectedAIFMask.IsNotNull()) { aifGenerator->SetMask(this->m_selectedAIFMask); } //image settings if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { this->m_selectedAIFImageNode = m_Controls.comboAIFImage->GetSelectedNode(); this->m_selectedAIFImage = dynamic_cast(this->m_selectedAIFImageNode->GetData()); } else { this->m_selectedAIFImageNode = m_selectedNode; this->m_selectedAIFImage = m_selectedImage; } aifGenerator->SetDynamicImage(this->m_selectedAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. View is in a invalide state. No AIF mode selected."; } } void PETDynamicView::LoadAIFfromFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.aifFilePath->setText(fileName); std::string m_aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::ifstream in1(m_aifFilePath.c_str()); if (!in1.is_open()) { m_Controls.errorMessageLabel->setText("Could not open AIF File!"); } std::vector< std::string > vec1; std::string line1; while (getline(in1, line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(), tok.end()); // if (vec1.size() < 3) continue; this->AIFinputGrid.push_back(convertToDouble(vec1[0])); this->AIFinputFunction.push_back(convertToDouble(vec1[1])); } }