Commit 623070be authored by Floris Berendsen's avatar Floris Berendsen
Browse files

Merge branch 'ELASTIX-143-Apply-Coerts-Comments-Overlord' into develop

parents 9a0548df 1653e947
......@@ -64,7 +64,6 @@ public:
}
BlueprintPointerType blueprint;
Overlord::Pointer overlord;
};
TEST_F(ElastixComponentTest, ImagesOnly)
......
......@@ -70,8 +70,6 @@ namespace selx {
class WBIRDemoTest : public ::testing::Test {
public:
typedef Overlord::Pointer OverlordPointerType;
/** Fill SUPERelastix' component data base by registering various components */
typedef TypeList <
DisplacementFieldItkImageFilterSinkComponent<2, float>,
......
/*=========================================================================
*
* Copyright Leiden University Medical Center, Erasmus University Medical
* Center and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
/** Helper definitions for common cstring key values to avoid typos*/
// In SuperElastix we use cstring keys to communicate between components. The
// advantage is that user defined cstring-based configurations can directly talk to the core.
// In the core we could type these names directly as literal cstrings, but this is sensitive to typos.
// By these definitions tools such as visual assist will auto-complete when typing keys::HasP...
namespace selx
{
namespace keys
{
const char * const NameOfInterface = "NameOfInterface"; // Each Interface has a sting name
const char * const HasProvidingInterface = "HasProvidingInterface"; // Checks component (base class) if the interface is present
const char * const HasAcceptingInterface = "HasAcceptingInterface"; // Checks component (base class) if the interface is present
const char * const SourceInterface = "SourceInterface"; // Special interface that connects to the outside of the SuperElastixFilter
const char * const SinkInterface = "SinkInterface"; // Special interface that connects to the outside of the SuperElastixFilter
}
}
\ No newline at end of file
......@@ -59,8 +59,11 @@ namespace selx
virtual int AcceptConnectionFrom(ComponentBase*) = 0;
/** if there is any failed criterion, return false (like a short - circuit AND) */
//TODO deprecate:
bool MeetsCriteria(const CriteriaType &criteria);
bool MeetsCriterionBase(const CriterionType &criterion);
virtual bool MeetsCriterion(const CriterionType &criterion) = 0;
protected:
virtual bool HasAcceptingInterface(const char *) = 0;
......
......@@ -52,24 +52,25 @@ public:
/** Convenient typedefs. */
typedef ComponentBase::Pointer ComponentBasePointer;
typedef ComponentBase::CriteriaType CriteriaType;
typedef ComponentBase::CriterionType CriterionType;
typedef std::list< ComponentBasePointer > ComponentListType;
typedef ComponentListType::size_type NumberOfComponentsType;
/** set selection criteria for possibleComponents*/
void Initialize();
void SetCriteria(const CriteriaType &criteria);
/** Narrow selection criteria*/
void AddCriteria(const CriteriaType &criteria);
NumberOfComponentsType UpdatePossibleComponents(void);
void AddCriterion(const CriterionType &criterion);
/** Check for multiple versus 1 or 0 components*/
bool HasMultipleComponents(void);
/** Return Component or Nullptr*/
ComponentBasePointer GetComponent(void);
protected:
mutable CriteriaType m_Criteria;
mutable ComponentListType m_PossibleComponents;
CriteriaType m_Criteria;
ComponentListType m_PossibleComponents;
ComponentSelector();
~ComponentSelector();
......
......@@ -42,77 +42,92 @@
namespace selx
{
class Overlord : public itk::LightProcessObject
class Overlord
{
public:
selxNewMacro(Overlord, itk::LightProcessObject);
typedef ComponentBase::CriteriaType CriteriaType;
typedef ComponentBase::CriterionType CriterionType;
typedef ComponentBase::ParameterValueType ParameterValueType;
typedef ComponentBase ComponentType;
typedef ComponentSelector::Pointer ComponentSelectorPointer;
typedef Blueprint BlueprintType;
typedef BlueprintType::ComponentNameType ComponentNameType;
typedef std::map< ComponentNameType, ComponentSelectorPointer> ComponentSelectorContainerType;
typedef ComponentSelectorContainerType::iterator ComponentSelectorIteratorType;
typedef AnyFileReader AnyFileReaderType;
typedef AnyFileWriter AnyFileWriterType;
typedef Blueprint::ComponentNameType ComponentNameType;
typedef std::map <
std::string, SourceInterface* > SourceInterfaceMapType;
typedef std::map <
std::string, SinkInterface* > SinkInterfaceMapType;
SourceInterfaceMapType GetSourceInterfaces();
SinkInterfaceMapType GetSinkInterfaces();
Overlord();
~Overlord() {};
SinkInterface::DataObjectPointer GetInitializedOutput(const Overlord::ComponentNameType&);
//void SetBlueprint(Blueprint const * blueprint){
// m_Blueprint = blueprint;
//}
typedef itk::VectorContainer <
unsigned int, ComponentType::Pointer > ComponentsContainerType;
void SetBlueprint(Blueprint::Pointer blueprint){
//Overlord should be constructed with a blueprint.
m_Blueprint = blueprint;
}
//TODO make const correct
//itkSetConstObjectMacro(Blueprint, Blueprint);
itkSetObjectMacro(Blueprint, Blueprint);
/** Read configuration at the blueprints nodes and edges and return true if all components could be uniquely selected*/
bool Configure();
bool Execute();
AnyFileReaderType::Pointer GetInputFileReader(const ComponentNameType&);
AnyFileWriterType::Pointer GetOutputFileWriter(const ComponentNameType&);
/** if all components are uniquely selected, they can be connected */
bool ConnectComponents();
/** Run the (registration) algorithm */
void Execute();
SourceInterfaceMapType GetSourceInterfaces();
SinkInterfaceMapType GetSinkInterfaces();
AnyFileReader::Pointer GetInputFileReader(const ComponentNameType&);
AnyFileWriter::Pointer GetOutputFileWriter(const ComponentNameType&);
SinkInterface::DataObjectPointer GetInitializedOutput(const Overlord::ComponentNameType&);
protected:
private:
typedef ComponentBase::CriteriaType CriteriaType;
typedef ComponentBase::CriterionType CriterionType;
typedef ComponentBase::ParameterValueType ParameterValueType;
typedef ComponentSelector::Pointer ComponentSelectorPointer;
Overlord();
virtual ~Overlord() {};
public: // temporarily from private to public during refactoring SuperElastixFilter.
typedef Blueprint::ComponentNamesType ComponentNamesType;
typedef std::map< ComponentNameType, ComponentSelectorPointer> ComponentSelectorContainerType;
typedef ComponentSelectorContainerType::iterator ComponentSelectorIteratorType;
/** Read configuration at the blueprints nodes and try to find instantiated components */
void ApplyNodeConfiguration();
/** Read configuration at the blueprints edges and try to find instantiated components */
void ApplyConnectionConfiguration();
bool UpdateSelectors();
bool ConnectComponents();
bool FindAfterRegistration();
bool FindRunRegistration();
private:
bool ConnectSources();
bool ConnectSinks();
bool RunRegistrations();
bool AfterRegistrations();
bool ReconnectTransforms();
/** See which components need more configuration criteria */
ComponentNamesType GetNonUniqueComponentNames();
//TODO make const correct
//Overlord should be constructed with a blueprint.
//Blueprint::ConstPointer m_Blueprint;
BlueprintType::Pointer m_Blueprint;
//Blueprint const * m_Blueprint;
Blueprint::Pointer m_Blueprint;
// A selector for each node, that each can hold multiple instantiated components. Ultimately is should be 1 component each.
ComponentSelectorContainerType m_ComponentSelectorContainer;
bool m_isConfigured;
bool m_allUniqueComponents;
/** Flow handling: todo move to a controller component */
void FindAfterRegistration();
void FindRunRegistration();
void RunRegistrations();
void AfterRegistrations();
void ReconnectTransforms();
typedef itk::VectorContainer <
unsigned int, ComponentBase::Pointer > ComponentsContainerType;
ComponentsContainerType::Pointer m_RunRegistrationComponents;
ComponentsContainerType::Pointer m_AfterRegistrationComponents;
};
......
......@@ -23,66 +23,43 @@ namespace selx
{
bool ComponentBase::MeetsCriteria(const CriteriaType &criteria)
bool ComponentBase::MeetsCriterionBase(const CriterionType &criterion)
{
bool hasUndefinedCriteria(false);
bool meetsCriteria(true);
for (CriteriaType::const_iterator it = criteria.begin(); it != criteria.cend(); ++it)
if (criterion.first == "NameOfClass")
{
if (it->first == "NameOfClass")
if (criterion.second.size() != 1)
{
if (it->second.size() != 1)
{
itkExceptionMacro("The criterion NameOfClass may have only 1 value");
}
auto temp = this->GetNameOfClass();
if (it->second[0] != this->GetNameOfClass())
{
meetsCriteria = false;
//break;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
itkExceptionMacro("The criterion NameOfClass may have only 1 value");
}
return (criterion.second[0] == this->GetNameOfClass());
}
else if (it->first == "HasAcceptingInterface")
else if (criterion.first == "HasAcceptingInterface")
{
for (const auto & value : criterion.second)
{
ParameterValueType::const_iterator valueIt;
const ParameterValueType::const_iterator valueItEnd = it->second.cend();
for (valueIt = it->second.cbegin(); valueIt != valueItEnd; ++valueIt)
if (this->HasAcceptingInterface(value.c_str()) == false)
{
if (this->HasAcceptingInterface(valueIt->c_str()) == false)
{
meetsCriteria = false;
//break;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
}
else if (it->first =="HasProvidingInterface")
return true;
}
else if (criterion.first == "HasProvidingInterface")
{
for (const auto & value : criterion.second)
{
ParameterValueType::const_iterator valueIt;
const ParameterValueType::const_iterator valueItEnd = it->second.cend();
for (valueIt = it->second.cbegin(); valueIt != valueItEnd; ++valueIt){
if (this->HasProvidingInterface(valueIt->c_str()) == false)
{
meetsCriteria = false;
//break;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
if (this->HasProvidingInterface(value.c_str()) == false)
{
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
}
else if (this->MeetsCriterion(CriterionType(it->first, it->second)) == false)
{
meetsCriteria = false;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
return true;
}
return meetsCriteria;
// else pass criterion to derived Component
return this->MeetsCriterion(criterion);
}
} // end namespace selx
......@@ -26,81 +26,34 @@ namespace selx
ComponentSelector::ComponentSelector()
{
this->m_PossibleComponents.clear();
}
ComponentSelector::~ComponentSelector()
{
}
std::list< itk::LightObject::Pointer > allobjects =
itk::ObjectFactoryBase::CreateAllInstance("ComponentBase");
void ComponentSelector::Initialize()
{
std::list< itk::LightObject::Pointer > allobjects =
itk::ObjectFactoryBase::CreateAllInstance("ComponentBase");
for (std::list< itk::LightObject::Pointer >::iterator i = allobjects.begin();
i != allobjects.end(); ++i)
{
ComponentBase *io =
dynamic_cast<ComponentBase *>(i->GetPointer());
if (io)
for (std::list< itk::LightObject::Pointer >::iterator i = allobjects.begin();
i != allobjects.end(); ++i)
{
this->m_PossibleComponents.push_back(io);
ComponentBase *io =
dynamic_cast<ComponentBase *>(i->GetPointer());
if (io)
{
this->m_PossibleComponents.push_back(io);
}
}
}
}
void ComponentSelector::SetCriteria(const CriteriaType &criteria)
{
this->Initialize();
this->m_Criteria = criteria;
this->Modified();
}
ComponentSelector::~ComponentSelector()
{
}
void ComponentSelector::AddCriteria(const CriteriaType &criteria)
void ComponentSelector::AddCriterion(const CriterionType &criterion)
{
this->m_Criteria.insert(criteria.begin(), criteria.end());
this->Modified();
this->m_PossibleComponents.remove_if([&](ComponentBasePointer component){ return !component->MeetsCriterionBase(criterion); });
}
// a predicate implemented as a class:
//struct FailsCriteria {
// bool operator() (const ComponentBasePointer& component) { return !component->MeetsCriteria(this->m_Criteria) }
//};
ComponentSelector::NumberOfComponentsType ComponentSelector::UpdatePossibleComponents()
{
// Check each possible component if it meets the criteria
// Using a Lambda function.
this->m_PossibleComponents.remove_if([&](ComponentBasePointer component){ return !component->MeetsCriteria(this->m_Criteria); });
const int numberOfComponents = this->m_PossibleComponents.size();
if (numberOfComponents == 0)
{
//TODO report about m_Criteria
std::stringstream message;
message << "Too many criteria for component. There is no component in our database that fulfills this set of criteria: " << std::endl;
for (CriteriaType::const_iterator criterion = this->m_Criteria.begin(); criterion != this->m_Criteria.cend(); ++criterion)
{
message << " " << criterion->first << " : { ";
for (auto const & criterionValue : criterion->second) // auto&& preferred?
{
message << criterionValue << " ";
}
message << "}" << std::endl;
}
std::cout << message.str();
//TODO how does this work for strings?
itkExceptionMacro("Too many criteria for component ");
}
return numberOfComponents;
}
ComponentSelector::ComponentBasePointer ComponentSelector::GetComponent()
{
//TODO check if Modified
this->UpdatePossibleComponents();
//this->UpdatePossibleComponents();
if (this->m_PossibleComponents.size() == 1)
{
......@@ -111,6 +64,12 @@ ComponentSelector::ComponentBasePointer ComponentSelector::GetComponent()
return ITK_NULLPTR;
}
}
bool ComponentSelector::HasMultipleComponents()
{
return (this->m_PossibleComponents.size() > 1);
}
} // end namespace selx
//#endif
......@@ -18,10 +18,11 @@
*=========================================================================*/
#include "Overlord.h"
#include "selxKeys.h"
namespace selx
{
Overlord::Overlord() : m_isConfigured(false), m_allUniqueComponents(false)
Overlord::Overlord() : m_isConfigured(false)
{
this->m_RunRegistrationComponents = ComponentsContainerType::New();
this->m_AfterRegistrationComponents = ComponentsContainerType::New();
......@@ -29,58 +30,58 @@ namespace selx
bool Overlord::Configure()
{
//TODO: make a while loop until stable:
// - for each unique component propagate the required interfaces to neighboring components as added criterion
if (!this->m_isConfigured)
{
this->m_isConfigured = true;
this->ApplyNodeConfiguration();
std::cout << "Applying Component Settings" << std::endl;
this->m_allUniqueComponents = this->UpdateSelectors();
std::cout << "Based on Component Criteria unique components could " << (this->m_allUniqueComponents ? "" : "not ") << "be selected" << std::endl;
std::cout << "Applying Connection Settings" << std::endl;
this->ApplyConnectionConfiguration();
this->m_allUniqueComponents = this->UpdateSelectors();
std::cout << "By adding Connection Criteria unique components could " << (this->m_allUniqueComponents ? "" : "not ") << "be selected" << std::endl;
//TODO: make a while loop until stable:
// - propagate for each unique component the required interfaces to neighboring components
// - update selectors
std::cout << "Applying Component Criteria" << std::endl;
this->ApplyNodeConfiguration();
//if (allUniqueComponents)
//{
// isSuccess = this->ConnectComponents();
//}
auto nonUniqueComponentNames = this->GetNonUniqueComponentNames();
std::cout << nonUniqueComponentNames.size() << " out of " << m_Blueprint->GetComponentNames().size() << " Components could not be uniquely selected" << std::endl << std::endl;
//std::cout << "Connecting Components: " << (isSuccess? "succeeded" : "failed") << std::endl;
std::cout << "Applying Connection Criteria" << std::endl;
this->ApplyConnectionConfiguration();
nonUniqueComponentNames = this->GetNonUniqueComponentNames();
std::cout << nonUniqueComponentNames.size() << " out of " << m_Blueprint->GetComponentNames().size() << " Components could not be uniquely selected" << std::endl << std::endl;
//return isSuccess;
this->m_isConfigured = true;
}
auto nonUniqueComponentNames = this->GetNonUniqueComponentNames();
if (nonUniqueComponentNames.size() > 0)
{
std::cout << std::endl << "These Nodes need more criteria: " << std::endl;
for (const auto & nonUniqueComponentName : nonUniqueComponentNames)
{
std::cout << nonUniqueComponentName<< std::endl;
}
return false;
}
return this->m_allUniqueComponents;
return true;
}
bool Overlord::UpdateSelectors()
Overlord::ComponentNamesType Overlord::GetNonUniqueComponentNames()
{
bool allUniqueComponents = true;
ComponentNamesType nonUniqueComponentNames;
const Blueprint::ComponentNamesType componentNames = m_Blueprint->GetComponentNames();
for (auto const & name : componentNames)
{
ComponentSelector::NumberOfComponentsType numberOfComponents = this->m_ComponentSelectorContainer[name]->UpdatePossibleComponents();
{
// The current idea of the configuration setup is that the number of
// possible components at a node can only be reduced by adding criteria.
// If a node has 0 possible components, the configuration is aborted (with an exception)
// If all nodes have exactly 1 possible component, no more criteria are needed.
//
// Design consideration: should the exception be thrown by this->m_ComponentSelectorContainer[name]->UpdatePossibleComponents()?
// The (failing) criteria can be printed as well.
if (numberOfComponents > 1)
if (this->m_ComponentSelectorContainer[name]->HasMultipleComponents()==true)
{
allUniqueComponents = false;
//std::cout << "To select a component for blueprint node " << name << " more critatia are required" << std::endl;
nonUniqueComponentNames.push_back(name);
}
std::cout << "blueprint node " << name << " has selected " << numberOfComponents << " components" << std::endl;
}
return allUniqueComponents;
return nonUniqueComponentNames;
}
void Overlord::ApplyNodeConfiguration()
......@@ -102,16 +103,31 @@ namespace selx
// in turn, hold 1 (or more) component.
Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
for (auto const & name : componentNames)
{
std::cout << " Blueprint Node: " << name << std::endl;
ComponentSelectorPointer currentComponentSelector = ComponentSelector::New();
Blueprint::ParameterMapType currentProperty = this->m_Blueprint->GetComponent(name);
currentComponentSelector->SetCriteria(currentProperty);
for (auto const & criterion : currentProperty)
{
std::cout << " " << criterion.first << ": " << criterion.second[0] << std::endl;
currentComponentSelector->AddCriterion(criterion);
}
if ((currentComponentSelector->HasMultipleComponents() == false) && (currentComponentSelector->GetComponent().IsNull()))
{
std::stringstream msg;
msg << "Too many criteria for Component " << name << std::endl;
std::runtime_error::runtime_error(msg.str());
}
// insert new element
this->m_ComponentSelectorContainer[name]=currentComponentSelector;
this->m_ComponentSelectorContainer[name] = currentComponentSelector;
}
return;
}
void Overlord::ApplyConnectionConfiguration()
{
Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
......@@ -123,16 +139,19 @@ namespace selx
Blueprint::ParameterMapType connectionProperties = this->m_Blueprint->GetConnection(name, outgoingName);
if (connectionProperties.count("NameOfInterface") > 0)
{
ComponentBase::CriteriaType additionalSourceCriteria;
additionalSourceCriteria.insert(ComponentBase::CriterionType("HasProvidingInterface", connectionProperties["NameOfInterface"]));
ComponentBase::CriteriaType additionalTargetCriteria;
additionalTargetCriteria.insert(ComponentBase::CriterionType("HasAcceptingInterface", connectionProperties["NameOfInterface"]));
this->m_ComponentSelectorContainer[name]->AddCriteria(additionalSourceCriteria);
this->m_ComponentSelectorContainer[outgoingName]->AddCriteria(additionalTargetCriteria);