Skip to content
Snippets Groups Projects
Commit 29f948e4 authored by Floris Berendsen's avatar Floris Berendsen
Browse files

ENH: Output displacement fields are handled by sink and overlord

parent fa6c6fe4
No related branches found
No related tags found
No related merge requests found
Showing
with 383 additions and 13 deletions
......@@ -24,6 +24,8 @@ set( ${MODULE}_SOURCE_FILES
${${MODULE}_SOURCE_DIR}/src/selxItkImageSink.cxx
${${MODULE}_SOURCE_DIR}/src/selxItkImageSourceFixed.cxx
${${MODULE}_SOURCE_DIR}/src/selxItkImageSourceMoving.cxx
${${MODULE}_SOURCE_DIR}/src/selxDisplacementFieldItkImageFilterSink.cxx
)
# Compile library
......
#ifndef selxDisplacementFieldItkImageFilterSink_h
#define selxDisplacementFieldItkImageFilterSink_h
#include "ComponentBase.h"
#include "Interfaces.h"
#include <string.h>
#include "elxMacro.h"
#include "itkImageFileWriter.h"
namespace selx
{
template<int Dimensionality, class TPixel>
class DisplacementFieldItkImageFilterSinkComponent :
public Implements <
Accepting< DisplacementFieldItkImageSourceInterface<Dimensionality, TPixel> >,
Providing < SinkInterface >
>
{
public:
elxNewMacro(DisplacementFieldItkImageFilterSinkComponent, ComponentBase);
itkStaticConstMacro(Dimensionality, unsigned int, Dimensionality);
DisplacementFieldItkImageFilterSinkComponent();
virtual ~DisplacementFieldItkImageFilterSinkComponent();
typedef TPixel PixelType;
typedef itk::Image<itk::Vector<PixelType, Dimensionality>, Dimensionality> DeformationFieldImageType;
typedef itk::ImageSource<DeformationFieldImageType> DeformationFieldItkImageSourceType;
typedef typename DeformationFieldItkImageSourceType::Pointer DeformationFieldItkImageSourcePointer;
virtual int Set(DisplacementFieldItkImageSourceInterface<Dimensionality, TPixel>*) override;
virtual bool ConnectToOverlordSink(itk::Object::Pointer) override;
virtual bool MeetsCriterion(const CriterionType &criterion) override;
static const char * GetDescription() { return "DisplacementFieldItkImageFilterSink Component"; };
private:
itk::ProcessObject::Pointer m_Sink;
typename itk::ImageFileWriter<itk::Image<itk::Vector<TPixel,Dimensionality>, Dimensionality>>::Pointer m_SinkWriter;
protected:
/* The following struct returns the string name of computation type */
/* default implementation */
static inline const std::string GetTypeNameString()
{
itkGenericExceptionMacro(<< "Unknown ScalarType" << typeid(TPixel).name());
// TODO: provide the user instructions how to enable the compilation of the component with the required template types (if desired)
// We might define an exception object that can communicate various error messages: for simple user, for developer user, etc
}
static inline const std::string GetPixelTypeNameString()
{
itkGenericExceptionMacro(<< "Unknown PixelType" << typeid(TPixel).name());
// TODO: provide the user instructions how to enable the compilation of the component with the required template types (if desired)
// We might define an exception object that can communicate various error messages: for simple user, for developer user, etc
}
};
template <>
inline const std::string
DisplacementFieldItkImageFilterSinkComponent<2, float>
::GetPixelTypeNameString()
{
return std::string("float");
}
template <>
inline const std::string
DisplacementFieldItkImageFilterSinkComponent<2, double>
::GetPixelTypeNameString()
{
return std::string("double");
}
template <>
inline const std::string
DisplacementFieldItkImageFilterSinkComponent<3, float>
::GetPixelTypeNameString()
{
return std::string("float");
}
template <>
inline const std::string
DisplacementFieldItkImageFilterSinkComponent<3, double>
::GetPixelTypeNameString()
{
return std::string("double");
}
} //end namespace selx
#ifndef ITK_MANUAL_INSTANTIATION
#include "selxDisplacementFieldItkImageFilterSink.hxx"
#endif
#endif // #define selxDisplacementFieldItkImageFilterSink_h
\ No newline at end of file
#include "selxDisplacementFieldItkImageFilterSink.h"
namespace selx
{
template<int Dimensionality, class TPixel>
DisplacementFieldItkImageFilterSinkComponent< Dimensionality, TPixel>::DisplacementFieldItkImageFilterSinkComponent()
{
this->m_Sink = nullptr;
this->m_SinkWriter = nullptr;
}
template<int Dimensionality, class TPixel>
DisplacementFieldItkImageFilterSinkComponent< Dimensionality, TPixel>::~DisplacementFieldItkImageFilterSinkComponent()
{
}
template<int Dimensionality, class TPixel>
int DisplacementFieldItkImageFilterSinkComponent< Dimensionality, TPixel>::Set(DisplacementFieldItkImageSourceInterface<Dimensionality, TPixel>* other)
{
if (this->m_SinkWriter == nullptr)
{
itkExceptionMacro("SinkComponent needs to be initialized by ConnectToOverlordSink()");
}
this->m_SinkWriter->SetInput(other->GetDisplacementFieldItkImageSource()->GetOutput());
return 0;
}
template<int Dimensionality, class TPixel>
bool DisplacementFieldItkImageFilterSinkComponent< Dimensionality, TPixel>::ConnectToOverlordSink(itk::Object::Pointer object)
{
bool anySuccessfulCast = false;
this->m_Sink = dynamic_cast<itk::ProcessObject*>(object.GetPointer());
anySuccessfulCast = this->m_Sink ? true : anySuccessfulCast;
this->m_SinkWriter = dynamic_cast<itk::ImageFileWriter<itk::Image<itk::Vector<TPixel, Dimensionality>, Dimensionality>>*>(object.GetPointer());
anySuccessfulCast = this->m_SinkWriter ? true : anySuccessfulCast;
return anySuccessfulCast;
}
template<int Dimensionality, class TPixel>
bool DisplacementFieldItkImageFilterSinkComponent< Dimensionality, TPixel>::MeetsCriterion(const CriterionType &criterion)
{
bool hasUndefinedCriteria(false);
bool meetsCriteria(false);
if (criterion.first == "ComponentProperty")
{
meetsCriteria = true;
for (auto const & criterionValue : criterion.second) // auto&& preferred?
{
if (criterionValue != "SomeProperty") // e.g. "GradientDescent", "SupportsSparseSamples
{
meetsCriteria = false;
}
}
}
else if (criterion.first == "Dimensionality") //Supports this?
{
meetsCriteria = true;
for (auto const & criterionValue : criterion.second) // auto&& preferred?
{
if (std::stoi(criterionValue) != Self::Dimensionality)
{
meetsCriteria = false;
}
}
}
else if (criterion.first == "PixelType") //Supports this?
{
meetsCriteria = true;
for (auto const & criterionValue : criterion.second) // auto&& preferred?
{
if (criterionValue != Self::GetPixelTypeNameString())
{
meetsCriteria = false;
}
}
}
if (criterion.first == "IsVectorField")
{
meetsCriteria = true;
for (auto const & criterionValue : criterion.second) // auto&& preferred?
{
if (criterionValue != "True") // e.g. "GradientDescent", "SupportsSparseSamples
{
meetsCriteria = false;
}
}
}
return meetsCriteria;
}
} //end namespace selx
#include "selxDisplacementFieldItkImageFilterSink.h"
......@@ -6,6 +6,7 @@
#include "itkImageRegistrationMethodv4.h"
#include "itkGradientDescentOptimizerv4.h"
#include "itkImageSource.h"
#include <itkTransformToDisplacementFieldFilter.h>
#include <string.h>
#include "elxMacro.h"
namespace selx
......@@ -17,7 +18,8 @@ namespace selx
itkImageSourceMovingInterface<Dimensionality, TPixel>,
itkMetricv4Interface<Dimensionality, TPixel>
>,
Providing< itkImageSourceInterface<Dimensionality, TPixel>,
Providing< itkImageSourceInterface<Dimensionality, TPixel>,
DisplacementFieldItkImageSourceInterface<Dimensionality, TPixel>,
RunRegistrationInterface
>
>
......@@ -42,23 +44,33 @@ namespace selx
typedef itk::ImageSource<ConnectionImageType> ItkImageSourceType;
typedef typename ItkImageSourceType::Pointer ItkImageSourcePointer;
typedef itk::Image<itk::Vector<PixelType, Dimensionality>, Dimensionality> DisplacementFieldImageType;
typedef itk::ImageSource<DisplacementFieldImageType>DisplacementFieldItkImageSourceType;
typedef typename DisplacementFieldItkImageSourceType::Pointer DisplacementFieldItkImageSourcePointer;
typedef itk::ImageRegistrationMethodv4<FixedImageType, MovingImageType> TheItkFilterType;
typedef itk::ResampleImageFilter<MovingImageType, ConnectionImageType> ResampleFilterType;
typedef itk::TransformToDisplacementFieldFilter<DisplacementFieldImageType> DisplacementFieldFilterType;
//Accepting Interfaces:
virtual int Set(itkImageSourceFixedInterface<Dimensionality, TPixel>*) override;
virtual int Set(itkImageSourceMovingInterface<Dimensionality, TPixel>*) override;
virtual int Set(itkMetricv4Interface<Dimensionality, TPixel>*) override;
//Providing Interfaces:
virtual ItkImageSourcePointer GetItkImageSource() override;
virtual DisplacementFieldItkImageSourcePointer GetDisplacementFieldItkImageSource() override;
virtual void RunRegistration() override;
//BaseClass methods
virtual bool MeetsCriterion(const CriterionType &criterion) override;
//static const char * GetName() { return "ItkImageRegistrationMethodv4"; } ;
static const char * GetDescription() { return "ItkImageRegistrationMethodv4 Component"; };
private:
typename TheItkFilterType::Pointer m_theItkFilter;
typename ResampleFilterType::Pointer m_resampler;
typename DisplacementFieldFilterType::Pointer m_DisplacementFieldFilter;
protected:
/* The following struct returns the string name of computation type */
/* default implementation */
......
......@@ -7,7 +7,7 @@ namespace selx
{
m_theItkFilter = TheItkFilterType::New();
m_resampler = ResampleFilterType::New();
m_DisplacementFieldFilter = DisplacementFieldFilterType::New();
//TODO: instantiating the filter in the constructor might be heavy for the use in component selector factory, since all components of the database are created during the selection process.
// we could choose to keep the component light weighted (for checking criteria such as names and connections) until the settings are passed to the filter, but this requires an additional initialization step.
}
......@@ -61,6 +61,9 @@ void ItkImageRegistrationMethodv4Component< Dimensionality, TPixel>::RunRegistra
this->m_resampler->SetOutputSpacing(fixedImage->GetSpacing());
this->m_resampler->SetOutputDirection(fixedImage->GetDirection());
this->m_resampler->SetDefaultPixelValue(0);
this->m_DisplacementFieldFilter->SetTransformInput(this->m_theItkFilter->GetTransformOutput());
this->m_DisplacementFieldFilter->SetReferenceImage(fixedImage);
}
template<int Dimensionality, class TPixel>
......@@ -69,6 +72,16 @@ typename ItkImageRegistrationMethodv4Component< Dimensionality, TPixel>::ItkImag
return (ItkImageSourcePointer) this->m_resampler;
}
template<int Dimensionality, class TPixel>
typename ItkImageRegistrationMethodv4Component< Dimensionality, TPixel>::DisplacementFieldItkImageSourcePointer ItkImageRegistrationMethodv4Component< Dimensionality, TPixel>::GetDisplacementFieldItkImageSource()
{
return (DisplacementFieldItkImageSourcePointer) this->m_DisplacementFieldFilter;
}
template<int Dimensionality, class TPixel>
bool
ItkImageRegistrationMethodv4Component< Dimensionality, TPixel>
......
......@@ -119,6 +119,17 @@ struct InterfaceName < GetItkImageInterface <D, TPixel> >
}
};
template <int D, class TPixel>
struct InterfaceName < DisplacementFieldItkImageSourceInterface <D, TPixel> >
{
static const char* Get()
{
return "DisplacementFieldItkImageSourceInterface";
}
};
template <>
struct InterfaceName < SourceInterface >
......
......@@ -82,6 +82,13 @@ namespace selx
virtual typename itk::Image<TPixel, Dimensionality>::Pointer GetItkImage() = 0;
};
template<int Dimensionality, class TPixel>
class DisplacementFieldItkImageSourceInterface {
// An interface that exposes that its internal filter is derived from itkImageSource
public:
virtual typename itk::ImageSource<itk::Image<itk::Vector< TPixel, Dimensionality >, Dimensionality>>::Pointer GetDisplacementFieldItkImageSource() = 0;
};
class SourceInterface {
// A special interface: the Overlord checks components for this type of interface.
// By this interface only Source Components can to talk to the Overlord.
......
......@@ -59,6 +59,16 @@ namespace selx
unsigned int, Writer3doubleType::Pointer > Writer3doubleContainerType;
typedef itk::ImageFileWriter<itk::Image<itk::Vector<float, 2>, 2>> WriterDisplacement2floatType;
typedef itk::ImageFileWriter<itk::Image<itk::Vector<double, 3>, 3>> WriterDisplacement3doubleType;
typedef itk::VectorContainer <
unsigned int, WriterDisplacement2floatType::Pointer > WriterDisplacement2floatContainerType;
typedef itk::VectorContainer <
unsigned int, WriterDisplacement3doubleType::Pointer > WriterDisplacement3doubleContainerType;
// typedef itk::Object::Pointer ObjectPointer;
// typedef itk::VectorContainer <
// unsigned int, ObjectPointer > ObjectContainerType;
......@@ -116,10 +126,14 @@ namespace selx
Writer3doubleContainerType::Pointer m_Writers3double;
Reader2floatContainerType::Pointer m_Readers2float;
Writer2floatContainerType::Pointer m_Writers2float;
WriterDisplacement2floatContainerType::Pointer m_WritersDisplacement2float;
WriterDisplacement3doubleContainerType::Pointer m_WritersDisplacement3double;
ComponentsContainerType::Pointer m_RunRegistrationComponents;
ComponentsContainerType::Pointer m_AfterRegistrationComponents;
};
} // end namespace selx
......
......@@ -14,6 +14,10 @@ namespace selx
this->m_Writers2float = Writer2floatContainerType::New();
this->m_Readers3double = Reader3doubleContainerType::New();
this->m_Writers3double = Writer3doubleContainerType::New();
this->m_WritersDisplacement2float = WriterDisplacement2floatContainerType::New();
this->m_WritersDisplacement3double = WriterDisplacement3doubleContainerType::New();
}
void Overlord::SetBlueprint(const Blueprint::Pointer blueprint)
......@@ -64,6 +68,16 @@ namespace selx
std::cout << "Found " << numberSinks3double << " Sink Components (3d double)" << std::endl;
}
int numberSinksDisplacement2float = this->m_WritersDisplacement2float->size(); // temporary solution
if (numberSinksDisplacement2float > 0)
{
std::cout << "Found " << numberSinksDisplacement2float << " Sink Components (Displacement 2d float)" << std::endl;
}
int numberSinksDisplacement3double = this->m_WritersDisplacement3double->size(); // temporary solution
if (numberSinksDisplacement3double > 0)
{
std::cout << "Found " << numberSinksDisplacement3double << " Sink Components (Displacement 3d double)" << std::endl;
}
if (allUniqueComponents)
......@@ -205,18 +219,14 @@ namespace selx
{
/** Scans all Components to find those with Sourcing capability and store them in SourceComponents list */
const CriterionType sourceCriterion = CriterionType("HasProvidingInterface", { "SourceInterface" });
int readercounter = 0; // temporary solution for reader filenames
// TODO redesign ComponentBase class to accept a single criterion instead of a criteria mapping.
CriteriaType sourceCriteria;
sourceCriteria.insert(sourceCriterion);
for (auto && componentSelector : (this->m_ComponentSelectorContainer))
{
ComponentBase::Pointer component = componentSelector->GetComponent();
if (component->MeetsCriteria(sourceCriteria)) // TODO MeetsCriterion
if (component->MeetsCriteria({ { "HasProvidingInterface", { "SourceInterface" } } })) // TODO MeetsCriterion
{
SourceInterface* provingSourceInterface = dynamic_cast<SourceInterface*> (&(*component));
if (provingSourceInterface == nullptr) // is actually a double-check for sanity: based on criterion cast should be successful
......@@ -225,8 +235,8 @@ namespace selx
}
//TODO: Make these connections available as inputs of the SuperElastix (filter).
CriteriaType typeCriteria = { { "Dimensionality", { "3" } }, { "PixelType", { "double" } } };
if (component->MeetsCriteria(typeCriteria))
if (component->MeetsCriteria({ { "Dimensionality", { "3" } }, { "PixelType", { "double" } } }))
{
// For now, we just create the readers here.
Reader3doubleType::Pointer reader;
......@@ -235,6 +245,10 @@ namespace selx
//filename << "C:\\wp\\SuperElastix\\bld2\\SuperElastix-build\\bin\\Debug\\sourceimage" << readercounter << ".mhd";
//filename << "source3dimage" << readercounter << ".mhd";
//reader->SetFileName(filename.str());
if (readercounter >= this->inputFileNames.size())
{
itkExceptionMacro("not enough inputFileNames provided")
}
reader->SetFileName(this->inputFileNames[readercounter]);
this->m_Readers3double->push_back(reader);
......@@ -252,6 +266,10 @@ namespace selx
//filename << "C:\\wp\\SuperElastix\\bld2\\SuperElastix-build\\bin\\Debug\\sourceimage" << readercounter << ".mhd";
//filename << "source2dimage" << readercounter << ".mhd";
//reader->SetFileName(filename.str());
if (readercounter >= this->inputFileNames.size())
{
itkExceptionMacro("not enough inputFileNames provided")
}
reader->SetFileName(this->inputFileNames[readercounter]);
this->m_Readers2float->push_back(reader);
......@@ -294,7 +312,37 @@ namespace selx
}
//TODO: Make these connections available as outputs of the SuperElastix (filter).
if (component->MeetsCriteria({ { "Dimensionality", { "3" } }, { "PixelType", { "double" } } }))
if (component->MeetsCriteria({ { "IsVectorField", { "True" } }, { "Dimensionality", { "3" } }, { "PixelType", { "double" } } }))
{
// For now, we just create the readers here.
WriterDisplacement3doubleType::Pointer writer;
writer = WriterDisplacement3doubleType::New();
if (writercounter >= this->inputFileNames.size())
{
itkExceptionMacro("not enough outputFileNames provided")
}
writer->SetFileName(this->outputFileNames[writercounter]);
this->m_WritersDisplacement3double->push_back(writer);
provingSinkInterface->ConnectToOverlordSink((itk::SmartPointer<itk::Object>) writer);
}
else if (component->MeetsCriteria({ { "IsVectorField", { "True" } }, { "Dimensionality", { "2" } }, { "PixelType", { "float" } } }))
{
// For now, we just create the readers here.
WriterDisplacement2floatType::Pointer writer;
writer = WriterDisplacement2floatType::New();
if (writercounter >= this->inputFileNames.size())
{
itkExceptionMacro("not enough inputFileNames provided")
}
writer->SetFileName(this->outputFileNames[writercounter]);
this->m_WritersDisplacement2float->push_back(writer);
provingSinkInterface->ConnectToOverlordSink((itk::SmartPointer<itk::Object>) writer);
}
else if (component->MeetsCriteria({ { "Dimensionality", { "3" } }, { "PixelType", { "double" } } }))
{
// For now, we just create the writers here.
Writer3doubleType::Pointer writer;
......@@ -302,6 +350,10 @@ namespace selx
//std::stringstream filename;
//filename << "sink3dimage" << writercounter << ".mhd";
//writer->SetFileName(filename.str());
if (writercounter >= this->outputFileNames.size())
{
itkExceptionMacro("not enough outputFileNames provided")
}
writer->SetFileName(this->outputFileNames[writercounter]);
this->m_Writers3double->push_back(writer);
......@@ -316,6 +368,10 @@ namespace selx
//std::stringstream filename;
//filename << "sink2dimage" << writercounter << ".mhd";
//writer->SetFileName(filename.str());
if (writercounter >= this->outputFileNames.size())
{
itkExceptionMacro("not enough outputFileNames provided")
}
writer->SetFileName(this->outputFileNames[writercounter]);
this->m_Writers2float->push_back(writer);
......
......@@ -10,6 +10,7 @@
#include "selxItkSmoothingRecursiveGaussianImageFilterComponent.h"
#include "selxItkImageFilterSink.h"
#include "selxDisplacementFieldItkImageFilterSink.h"
#include "selxItkImageSource.h"
......@@ -44,6 +45,7 @@ public:
ComponentFactory<ItkImageFilterSinkComponent<3,double>>::RegisterOneFactory();
ComponentFactory<DisplacementFieldItkImageFilterSinkComponent<3, double>>::RegisterOneFactory();
ComponentFactory<ItkImageSourceComponent>::RegisterOneFactory();
ComponentFactory<ItkImageSourceFixedComponent<2, float>>::RegisterOneFactory();
......@@ -231,4 +233,60 @@ TEST_F(RegistrationItkv4Test, WithMeanSquaresMetric)
EXPECT_NO_THROW(overlord->Execute());
//overlord->Execute();
}
TEST_F(RegistrationItkv4Test, ImagesOnlyGetDisplacementField)
{
/** make example blueprint configuration */
blueprint = Blueprint::New();
ParameterMapType component0Parameters;
component0Parameters["NameOfClass"] = { "ItkImageRegistrationMethodv4Component" };
ComponentIndexType index0 = blueprint->AddComponent(component0Parameters);
ParameterMapType component1Parameters;
component1Parameters["NameOfClass"] = { "ItkImageSourceFixedComponent" };
component1Parameters["Dimensionality"] = { "3" }; // should be derived from the inputs
ComponentIndexType index1 = blueprint->AddComponent(component1Parameters);
ParameterMapType component2Parameters;
component2Parameters["NameOfClass"] = { "ItkImageSourceMovingComponent" };
component2Parameters["Dimensionality"] = { "3" }; // should be derived from the inputs
ComponentIndexType index2 = blueprint->AddComponent(component2Parameters);
ParameterMapType component3Parameters;
component3Parameters["NameOfClass"] = { "ItkImageFilterSinkComponent" };
//component3Parameters["Dimensionality"] = { "3" }; // should be derived from the outputs
ComponentIndexType index3 = blueprint->AddComponent(component3Parameters);
ParameterMapType component4Parameters;
component4Parameters["NameOfClass"] = { "DisplacementFieldItkImageFilterSinkComponent" };
//component3Parameters["Dimensionality"] = { "3" }; // should be derived from the outputs
ComponentIndexType index4 = blueprint->AddComponent(component4Parameters);
ParameterMapType connection1Parameters;
connection1Parameters["NameOfInterface"] = { "itkImageSourceFixedInterface" };
blueprint->AddConnection(index1, index0, connection1Parameters);
ParameterMapType connection2Parameters;
connection2Parameters["NameOfInterface"] = { "itkImageSourceMovingInterface" };
blueprint->AddConnection(index2, index0, connection2Parameters);
ParameterMapType connection3Parameters;
connection3Parameters["NameOfInterface"] = { "itkImageSourceInterface" };
blueprint->AddConnection(index0, index3, connection3Parameters);
ParameterMapType connection4Parameters;
connection4Parameters["NameOfInterface"] = { "DisplacementFieldItkImageSourceInterface" };
blueprint->AddConnection(index0, index4, connection4Parameters);
EXPECT_NO_THROW(overlord = Overlord::New());
overlord->inputFileNames = { "source3dimage0.mhd", "source3dimage1.mhd" };
overlord->outputFileNames = { "sink3dimage0.mhd", "sink3ddeformationfield1.mhd" };
EXPECT_NO_THROW(overlord->SetBlueprint(blueprint));
bool allUniqueComponents;
EXPECT_NO_THROW(allUniqueComponents = overlord->Configure());
EXPECT_TRUE(allUniqueComponents);
EXPECT_NO_THROW(overlord->Execute());
}
} // namespace elx
......@@ -90,6 +90,8 @@ public:
TEST_F(itkImageFilterTest, Run)
{
overlord = Overlord::New();
overlord->inputFileNames = { "source3dimage0.mhd" };
overlord->outputFileNames = { "itkImageFilterTest_output.mhd" };
overlord->SetBlueprint(blueprint);
bool allUniqueComponents;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment