Commit fa417b6f authored by Floris Berendsen's avatar Floris Berendsen
Browse files

Merge branch 'ELASTIX-52-Run-elastix-inside-SuperElastix-as-a-Component' into develop

Conflicts:
	SuperBuild/ExternalElastix.cmake
parents 99dffd1a 271abbfb
......@@ -51,7 +51,7 @@ macro( _elxmodules_initialize )
"${CMAKE_SOURCE_DIR}/Modules/*/Module*.cmake"
)
message( STATUS "Found the following elastix modules:")
message( STATUS "Found the following SuperElastix modules:")
foreach( MODULE_FILE ${MODULE_FILES})
get_filename_component( MODULE_NAME ${MODULE_FILE} NAME_WE )
get_filename_component( ${MODULE_NAME}_PATH ${MODULE_FILE} PATH )
......
......@@ -71,8 +71,10 @@ include( elxModules )
elxmodule_enable( ModuleCore )
elxmodule_enable( ModuleElastix )
elxmodule_enable( ModuleExamples )
elxmodule_enable( ModuleSinksAndSources )
elxmodule_enable( ModuleItkSmoothingRecursiveGaussianImageFilter )
elxmodule_enable( ModuleItkImageRegistrationMethodv4 )
elxmodule_enable( ModuleElastixComponent )
# TODO: Build tests depending on enabled modules
# ---------------------------------------------------------------------
......
# Module that exposes old elastix as an ITK filter
set( MODULE ModuleElastix )
# If OpenMP is supported, elastix will have beeen compiled with
# If OpenMP is supported, elastix will have been compiled with
# OpenMP flags, and we need to add them here as well
find_package( OpenMP )
if( OPENMP_FOUND )
......
set( MODULE ModuleElastixComponent )
# Find UseElastix.cmake
if( NOT EXISTS ${ELASTIX_USE_FILE} )
set( ELASTIX_USE_FILE ${ELASTIX_DIR}/UseElastix.cmake )
endif()
if( NOT EXISTS ${ELASTIX_USE_FILE} )
set( ELASTIX_DIR "" CACHE PATH "Path to elastix build folder" )
message(FATAL_ERROR "Could not find UseElastix.cmake. Point ELASTIX_DIR to folder containing UseElastix.cmake or use SuperBuild.")
endif()
# Export include files
include( ${ELASTIX_USE_FILE} )
# Export include files
set( ${MODULE}_INCLUDE_DIRS
${${MODULE}_SOURCE_DIR}/include
)
# Collect header files for Visual Studio Project
file(GLOB ${MODULE}_HEADER_FILES "${${MODULE}_SOURCE_DIR}/include/*.*")
# Export libraries
set( ${MODULE}_LIBRARIES
${MODULE}
)
# Export tests
set( ${MODULE}_TESTS
elxElastixComponentTest.cxx
)
# Module source files
set( ${MODULE}_SOURCE_FILES
${${MODULE}_SOURCE_DIR}/src/selxElastixComponent.cxx
)
# Compile library
add_library( ${MODULE} STATIC ${${MODULE}_SOURCE_FILES} ${${MODULE}_HEADER_FILES})
target_link_libraries( ${MODULE} ${ELASTIX_LIBRARIES} )
#ifndef selxElastixComponent_h
#define selxElastixComponent_h
#include "ComponentBase.h"
#include "Interfaces.h"
#include "itkImageSource.h"
#include "elxElastixFilter.h"
#include "elxParameterObject.h"
#include "elxTransformixFilter.h"
#include <string.h>
#include "elxMacro.h"
namespace selx
{
template <int Dimensionality, class TPixel>
class ElastixComponent :
public Implements<
Accepting<
itkImageSourceFixedInterface<Dimensionality, TPixel>,
itkImageSourceMovingInterface<Dimensionality, TPixel>
>,
Providing<
GetItkImageInterface<Dimensionality, TPixel>, //Since elastixFilter is not a true itkfilter we cannot use itkImageSourceInterface (yet)
RunRegistrationInterface
>
>
{
public:
elxNewMacro(ElastixComponent, ComponentBase);
//itkStaticConstMacro(Dimensionality, unsigned int, Dimensionality);
ElastixComponent();
virtual ~ElastixComponent();
typedef typename ComponentBase::CriterionType CriterionType;
typedef TPixel PixelType;
// the in and output image type of the component are chosen to be the same
typedef itk::Image<PixelType, Dimensionality> ConnectionImageType;
// fixed and moving image types are all the same, these aliases can be used to be explicit.
typedef itk::Image<PixelType, Dimensionality> FixedImageType;
typedef itk::Image<PixelType, Dimensionality> MovingImageType;
typedef itk::ImageSource<ConnectionImageType> ItkImageSourceType;
typedef typename ItkImageSourceType::Pointer ItkImageSourcePointer;
typedef typename ConnectionImageType::Pointer ItkImagePointer;
typedef elastix::ElastixFilter< FixedImageType, MovingImageType > TheItkFilterType;
typedef elastix::ParameterObject elxParameterObjectType;
typedef elxParameterObjectType::Pointer elxParameterObjectPointer;
typedef elastix::TransformixFilter<FixedImageType> TransformixFilterType;
typedef itk::ResampleImageFilter<MovingImageType, ConnectionImageType> ResampleFilterType;
virtual int Set(itkImageSourceFixedInterface<Dimensionality, TPixel>*) override;
virtual int Set(itkImageSourceMovingInterface<Dimensionality, TPixel>*) override;
//Since elastixFilter is not a true itkfilter we cannot use itkImageSourceInterface (yet)
//virtual ItkImageSourcePointer GetItkImageSource() override;
virtual ItkImagePointer GetItkImage() override;
virtual void RunRegistration() override;
virtual bool MeetsCriterion(const CriterionType &criterion) override;
static const char * GetDescription() { return "Elastix Component"; };
private:
typename TheItkFilterType::Pointer m_theItkFilter;
//elxParameterObjectPointer m_ParameterObject;
ItkImagePointer m_OutputImage;
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
}
};
// unfortunately partial specialization of member functions is not allowed, without partially specializing the entire class.
/*
template <int Dimensionality>
class ElastixComponent < Dimensionality, double >
{
static inline const std::string GetPixelTypeNameString();
};
template <int Dimensionality>
inline const std::string
ElastixComponent<Dimensionality, double>
::GetPixelTypeNameString()
{
return std::string("double");
}
*/
template <>
inline const std::string
ElastixComponent<2, float>
::GetPixelTypeNameString()
{
return std::string("float");
}
template <>
inline const std::string
ElastixComponent<2, double>
::GetPixelTypeNameString()
{
return std::string("double");
}
template <>
inline const std::string
ElastixComponent<3, float>
::GetPixelTypeNameString()
{
return std::string("float");
}
template <>
inline const std::string
ElastixComponent<3, double>
::GetPixelTypeNameString()
{
return std::string("double");
}
template <>
inline const std::string
ElastixComponent<2, float>
::GetTypeNameString()
{
return std::string("2_float");
}
template <>
inline const std::string
ElastixComponent<2, double>
::GetTypeNameString()
{
return std::string("2_double");
}
template <>
inline const std::string
ElastixComponent<3,float>
::GetTypeNameString()
{
return std::string("3_float");
}
template <>
inline const std::string
ElastixComponent<3,double>
::GetTypeNameString()
{
return std::string("3_double");
}
} //end namespace selx
#ifndef ITK_MANUAL_INSTANTIATION
#include "selxElastixComponent.hxx"
#endif
#endif // #define GDOptimizer3rdPartyComponent_h
\ No newline at end of file
#include "selxElastixComponent.h"
namespace selx
{
template<int Dimensionality, class TPixel>
ElastixComponent< Dimensionality, TPixel>::ElastixComponent()
{
m_theItkFilter = TheItkFilterType::New();
// TODO: Due to some issues with Criteria being propagated as elastix settings, I need to empty the elxparameterObject.
elxParameterObjectPointer elxparameterObject = elxParameterObjectType::New();
elxparameterObject->SetParameterMap("rigid");
this->m_theItkFilter->SetParameterObject(elxparameterObject);
m_theItkFilter->LogToConsoleOn();
//m_ParameterObject->SetParameterMap("rigid");
//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.
}
template<int Dimensionality, class TPixel>
ElastixComponent< Dimensionality, TPixel>::~ElastixComponent()
{
}
template<int Dimensionality, class TPixel>
int ElastixComponent< Dimensionality, TPixel>::Set(itkImageSourceFixedInterface<Dimensionality, TPixel>* component)
{
auto other = component->GetItkImageSourceFixed();
// connect the itk pipeline
this->m_theItkFilter->SetFixedImage(other->GetOutput());
return 1;
}
template<int Dimensionality, class TPixel>
int ElastixComponent< Dimensionality, TPixel>::Set(itkImageSourceMovingInterface<Dimensionality, TPixel>* component)
{
auto other = component->GetItkImageSourceMoving();
// connect the itk pipeline
this->m_theItkFilter->SetMovingImage(other->GetOutput());
return 1;
}
//Since elastixFilter is not a true itkfilter we cannot use itkImageSourceInterface (yet)
/*template<int Dimensionality, class TPixel>
typename ElastixComponent< Dimensionality, TPixel>::ItkImageSourcePointer ElastixComponent< Dimensionality, TPixel>::GetItkImageSource()
{
return (ItkImageSourcePointer) this->m_theItkFilter;
}
*/
template<int Dimensionality, class TPixel>
typename ElastixComponent< Dimensionality, TPixel>::ItkImagePointer ElastixComponent< Dimensionality, TPixel>::GetItkImage()
{
// We cannot just call return this->m_theItkFilter->GetOutput(), since the network is generally not ready for execution during the handshake
return this->m_OutputImage;
}
template<int Dimensionality, class TPixel>
void ElastixComponent< Dimensionality, TPixel>::RunRegistration(void)
{
//this->m_theItkFilter->Update();
this->m_OutputImage = this->m_theItkFilter->GetOutput();
typename TransformixFilterType::Pointer transformixFilter;
transformixFilter = TransformixFilterType::New();
// In the current transformix filter an input image is required even if we want a deformation field only.
transformixFilter->SetInputImage(this->m_theItkFilter->GetOutput());
transformixFilter->SetTransformParameterObject(this->m_theItkFilter->GetTransformParameterObject());
//transformixFilter->SetOutputDirectory(dataManager->GetOutputDirectory());
transformixFilter->ComputeDeformationFieldOn();
transformixFilter->LogToConsoleOn();
transformixFilter->LogToFileOn();
transformixFilter->Update();
//ImageFileWriterType::Pointer writer = ImageFileWriterType::New();
//writer->SetFileName(dataManager->GetOutputFile("Euler2DTransformixResultImage.nii"));
//writer->SetInput(transformixFilter->GetOutput());
//writer->Update();
}
template<int Dimensionality, class TPixel>
bool
ElastixComponent< 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) != 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;
}
}
}
else if (criterion.first == "RegistrationPreset") //Supports this?
{
// Temporary solution: RegistrationSettings: rigid, nonrigid, etc overwrite the current elxparameterObject.
// Warning: the order of Criteria matters, since elxparameterObject may be overwritten
// Warning: this probably fails because the Criteria map entries are processed in arbitrary order.
elxParameterObjectPointer elxparameterObject = elxParameterObjectType::New();
meetsCriteria = true;
for (auto const & transformtype : criterion.second) // auto&& preferred?
{
//this->m_ParameterObject->AddParameterMap(transformtype, const unsigned int numberOfResolutions = 3u, const double finalGridSpacingInPhysicalUnits = 10.0);
elxparameterObject->SetParameterMap(transformtype);
try
{
this->m_theItkFilter->SetParameterObject(elxparameterObject);
}
catch (itk::ExceptionObject & err)
{
std::cout << err;
//TODO log the error message?
meetsCriteria = false;
}
}
}
else
{
// temporary solution: pass all SuperElastixComponent parameters as is to elastix. This should be defined in deeper hierarchy of the criteria, but for now we have a flat mapping only.
elxParameterObjectPointer elxparameterObject = this->m_theItkFilter->GetParameterObject();
elxparameterObject->GetParameterMap(0)[criterion.first] = criterion.second;
this->m_theItkFilter->SetParameterObject(elxparameterObject);
meetsCriteria = true;
}
return meetsCriteria;
}
} //end namespace selx
......@@ -20,8 +20,8 @@ set( ${MODULE}_TESTS
# Module source files
set( ${MODULE}_SOURCE_FILES
${${MODULE}_SOURCE_DIR}/src/selxItkImageSource.cxx
${${MODULE}_SOURCE_DIR}/src/selxItkImageSink.cxx)
${${MODULE}_SOURCE_DIR}/src/selxItkSmoothingRecursiveGaussianImageFilterComponent.cxx
)
# Compile library
......
#ifndef selxItkImageSink_h
#define selxItkImageSink_h
#include "ComponentBase.h"
#include "Interfaces.h"
#include <string.h>
#include "elxMacro.h"
#include "itkImageFileWriter.h"
namespace selx
{
class ItkImageSinkComponent :
public Implements <
Accepting< itkImageSourceInterface<3,double> >,
Providing < SinkInterface >
>
{
public:
elxNewMacro(ItkImageSinkComponent, ComponentBase);
ItkImageSinkComponent();
virtual ~ItkImageSinkComponent();
typedef itk::ImageSource<itk::Image<double, 3>> ItkImageSourceType;
virtual int Set(itkImageSourceInterface<3, double>*) override;
virtual bool ConnectToOverlordSink(itk::Object::Pointer) override;
virtual bool MeetsCriterion(const CriterionType &criterion) override;
static const char * GetDescription() { return "ItkImageSink Component"; };
private:
itk::ProcessObject::Pointer m_Sink;
itk::ImageFileWriter<itk::Image<double, 3>>::Pointer m_SinkWriter;
};
} //end namespace selx
#endif // #define GDOptimizer3rdPartyComponent_h
\ No newline at end of file
......@@ -23,7 +23,7 @@ namespace selx
public:
elxNewMacro(ItkSmoothingRecursiveGaussianImageFilterComponent, ComponentBase);
itkStaticConstMacro(Dimensionality, unsigned int, Dimensionality);
// itkStaticConstMacro(Dimensionality, unsigned int, Dimensionality);
ItkSmoothingRecursiveGaussianImageFilterComponent();
virtual ~ItkSmoothingRecursiveGaussianImageFilterComponent();
......@@ -46,7 +46,7 @@ namespace selx
//int Update();
//virtual bool MeetsCriteria(const CriteriaType &criteria);
virtual bool MeetsCriterion(const CriterionType &criterion) override;
virtual bool MeetsCriterion(const ComponentBase::CriterionType &criterion) override;
//static const char * GetName() { return "GDOptimizer3rdPartyComponent"; } ;
static const char * GetDescription() { return "ItkSmoothingRecursiveGaussianImageFilter Component"; };
private:
......
......@@ -58,7 +58,7 @@ typename ItkSmoothingRecursiveGaussianImageFilterComponent< Dimensionality, TPix
template<int Dimensionality, class TPixel>
bool
ItkSmoothingRecursiveGaussianImageFilterComponent< Dimensionality, TPixel>
::MeetsCriterion(const CriterionType &criterion)
::MeetsCriterion(const ComponentBase::CriterionType &criterion)
{
bool hasUndefinedCriteria(false);
bool meetsCriteria(false);
......@@ -78,7 +78,7 @@ ItkSmoothingRecursiveGaussianImageFilterComponent< Dimensionality, TPixel>
meetsCriteria = true;
for (auto const & criterionValue : criterion.second) // auto&& preferred?
{
if (std::stoi(criterionValue) != Self::Dimensionality)
if (std::stoi(criterionValue) != Dimensionality)
{
meetsCriteria = false;
}
......
set( MODULE ModuleSinksAndSources )
# Export include files
set( ${MODULE}_INCLUDE_DIRS
${${MODULE}_SOURCE_DIR}/include
)
# Collect header files for Visual Studio Project
file(GLOB ${MODULE}_HEADER_FILES "${${MODULE}_SOURCE_DIR}/include/*.*")
# Export libraries
set( ${MODULE}_LIBRARIES
${MODULE}
)
# Export tests
set( ${MODULE}_TESTS
)
# Module source files
set( ${MODULE}_SOURCE_FILES
${${MODULE}_SOURCE_DIR}/src/selxItkImageSource.cxx
${${MODULE}_SOURCE_DIR}/src/selxItkImageFilterSink.cxx
${${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
add_library( ${MODULE} STATIC "${${MODULE}_SOURCE_FILES}" ${${MODULE}_HEADER_FILES})
target_link_libraries( ${MODULE} ${ELASTIX_LIBRARIES} )
#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;