diff --git a/CMake/elxModules.cmake b/CMake/elxModules.cmake index 7ba40b4dcd590ce7de87e6203ca71fef82ffdd5f..894204aebbf5c5098a57bfd09d967c535fee639b 100644 --- a/CMake/elxModules.cmake +++ b/CMake/elxModules.cmake @@ -6,43 +6,44 @@ macro( _elxmodule_check_name MODULE ) message( FATAL_ERROR "Invalid module name: ${MODULE}" ) endif() - list( FIND ELASTIX_MODULES "${MODULE}" MODULE_FOUND ) + list( FIND SUPERELASTIX_MODULES "${MODULE}" MODULE_FOUND ) if( ${MODULE_FOUND} EQUAL -1 ) message( FATAL_ERROR "Module not found: ${MODULE}") endif() endmacro() -macro( _elxmodule_enable MODULE ) - _elxmodule_check_name( ${MODULE} ) +macro( _elxmodule_enable MODULE_NAME ) + _elxmodule_check_name( ${MODULE_NAME} ) - if( NOT ${MODULE}_ENABLED ) - set( ELASTIX_USE_${MODULE} ON ) + if( NOT ${MODULE_NAME}_ENABLED ) + set( USE_${MODULE_NAME} ON ) - include( ${${MODULE}_FILE} ) + include( ${${MODULE_NAME}_FILE} ) - if( ${MODULE}_INCLUDE_DIRS ) - include_directories( ${${MODULE}_INCLUDE_DIRS} ) + if( ${MODULE_NAME}_INCLUDE_DIRS ) + include_directories( ${${MODULE_NAME}_INCLUDE_DIRS} ) endif() - if( ${MODULE}_LIBRARIES ) - link_directories( ${${MODULE}_LIBRARY_DIRS} ) - list( APPEND ELASTIX_LIBRARIES ${${MODULE}_LIBRARIES} ) + if( ${MODULE_NAME}_LIBRARIES ) + link_directories( ${${MODULE_NAME}_LIBRARY_DIRS} ) + list( APPEND SUPERELASTIX_LIBRARIES ${${MODULE_NAME}_LIBRARIES} ) endif() - # TODO: Add recursive dependency walk + # TODO: Add support for indicating dependencies between modules and recursive enabling of these dependencies + endif() endmacro() -macro( _elxmodule_disable MODULE ) +macro( _elxmodule_disable MODULE_NAME ) # TODO endmacro() macro( _elxmodules_initialize ) - set( ELASTIX_LIBRARIES ) - set( ELASTIX_MODULES ) + set( SUPERELASTIX_LIBRARIES ) + set( SUPERELASTIX_MODULES ) file( GLOB_RECURSE MODULE_FILES RELATIVE "${CMAKE_SOURCE_DIR}" - "${CMAKE_SOURCE_DIR}/Modules/*/elxModule*.cmake" + "${CMAKE_SOURCE_DIR}/Modules/*/Module*.cmake" ) message( STATUS "Found the following elastix modules:") @@ -52,7 +53,7 @@ macro( _elxmodules_initialize ) message( STATUS " ${MODULE_NAME}" ) - option( "ELASTIX_USE_${MODULE_NAME}" OFF ) + option( "USE_${MODULE_NAME}" OFF ) set( "${MODULE_NAME}_FILE" ${CMAKE_SOURCE_DIR}/${MODULE_FILE} ) set( "${MODULE_NAME}_ENABLED" OFF ) @@ -63,7 +64,7 @@ macro( _elxmodules_initialize ) set( ${MODULE_NAME}_LIBRARY_DIRS ) set( ${MODULE_NAME}_LIBRARIES ) - list(APPEND ELASTIX_MODULES ${MODULE_NAME} ) + list(APPEND SUPERELASTIX_MODULES ${MODULE_NAME} ) endforeach() endmacro() diff --git a/CMakeLists.txt b/CMakeLists.txt index 131af57444895cdd68d6e693e866856b0dac2b8f..beb8141ad9689fadb6f62395acdffa6718164cb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ include_directories( ${Boost_INCLUDE_DIRS} ) # Build SuperElastix # For now we just enable all modules include( elxModules ) -elxmodule_enable( elxModuleCore ) +elxmodule_enable( ModuleCore ) +elxmodule_enable( ModuleElastix ) # --------------------------------------------------------------------- # Testing diff --git a/Modules/Components/Elastix/ModuleElastix.cmake b/Modules/Components/Elastix/ModuleElastix.cmake new file mode 100644 index 0000000000000000000000000000000000000000..62e52cbe820ec5017440d40d59e32e9de35f8529 --- /dev/null +++ b/Modules/Components/Elastix/ModuleElastix.cmake @@ -0,0 +1,20 @@ +# Module that exposes the old elastix as a component +set( MODULE ModuleElastix ) + +if( NOT EXISTS ${ELASTIX_USE_FILE} ) + set( ELASTIX_USE_FILE ) + message( FATAL_ERROR "${MODULE} could not find ELASTIX_USE_FILE. Use the SuperBuild or manually point the ELASTIX_USE_FILE CMake variable to the UseElastix.cmake file in the root of your elastix build tree." ) +endif() + +include( ${ELASTIX_USE_FILE} ) + +# Export include files +set( ${MODULE}_INCLUDE_DIRS + ${${MODULE}_SOURCE_DIR}/include +) + +# Export libraries +set( ${MODULE}_LIBRARIES + elastix + transformix +) diff --git a/Modules/Components/Elastix/include/elxElastixFilter.h b/Modules/Components/Elastix/include/elxElastixFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..3ae5102b1b0e56e06e9c8bd75c45b76cec9c0cf2 --- /dev/null +++ b/Modules/Components/Elastix/include/elxElastixFilter.h @@ -0,0 +1,87 @@ +#ifndef ElastixComponent_h +#define ElastixComponent_h + +// ITK +#include "itkProcessObject.h" +#include "itkImageSource.h" + +// Elastix +#include "elxElastixMain.h" + +// SuperElastix +#include "elxMacro.h" +#include "elxParameterObject.h" + +namespace selx { + +template< typename TFixedImage, + typename TMovingImage, + typename TOutputImage > +class ElastixFilter : public itk::ImageSource< TOutputImage > +{ +public: + + elxNewMacro( ElastixFilter, itk::ImageSource ); + + typedef elastix::ElastixMain ElastixMainType; + typedef ElastixMainType::Pointer ElastixMainPointer; + typedef std::vector< ElastixMainPointer > ElastixMainVectorType; + typedef ElastixMainType::ObjectPointer ElastixMainObjectPointer; + typedef ElastixMainType::FlatDirectionCosinesType FlatDirectionCosinesType; + typedef ElastixMainType::ArgumentMapType ArgumentMapType; + typedef ArgumentMapType::value_type ArgumentMapEntryType; + + typedef itk::ProcessObject::DataObjectIdentifierType DataObjectIdentifierType; + typedef ElastixMainType::DataObjectContainerType DataObjectContainerType; + typedef ElastixMainType::DataObjectContainerPointer DataObjectContainerPointer; + + typedef ParameterObject::ParameterMapListType ParameterMapListType; + typedef ParameterObject::ParameterMapType ParameterMapType; + typedef ParameterObject::ParameterValuesType ParameterValuesType; + typedef typename ParameterObject::Pointer ParameterObjectPointer; + + typedef typename TFixedImage::Pointer FixedImagePointer; + typedef typename TMovingImage::Pointer MovingImagePointer; + + void SetFixedImage( FixedImagePointer fixedImage ); + void SetFixedImage( DataObjectContainerPointer fixedImage ); + void SetMovingImage( MovingImagePointer movingImage ); + void SetMovingImage( DataObjectContainerPointer movingImage ); + void SetParameterObject( ParameterObjectPointer parameterObject ); + void SetFixedMask( FixedImagePointer fixedMask ); + void SetMovingMask( MovingImagePointer movingMask ); + + ParameterObjectPointer GetTransformParameters( void ); + + itkSetMacro( LogToConsole, bool ); + itkGetMacro( LogToConsole, bool ); + itkBooleanMacro( LogToConsole ); + + itkSetMacro( LogToFile, std::string ); + itkGetMacro( LogToFile, std::string ); + +protected: + + void GenerateData( void ) ITK_OVERRIDE; + +private: + + ElastixFilter(); + + DataObjectContainerPointer m_FixedImageContainer; + DataObjectContainerPointer m_MovingImageContainer; + DataObjectContainerPointer m_FixedMaskContainer; + DataObjectContainerPointer m_MovingMaskContainer; + + bool m_LogToConsole; + std::string m_LogToFile; + +}; + +} // namespace selx + +#ifndef ITK_MANUAL_INSTANTIATION +#include "elxElastixFilter.hxx" +#endif + +#endif // ElastixFilter_h \ No newline at end of file diff --git a/Modules/Components/Elastix/include/elxElastixFilter.hxx b/Modules/Components/Elastix/include/elxElastixFilter.hxx new file mode 100644 index 0000000000000000000000000000000000000000..1cacc6e9bac66f1b06d9ac37de4c46aca5142a51 --- /dev/null +++ b/Modules/Components/Elastix/include/elxElastixFilter.hxx @@ -0,0 +1,237 @@ +#ifndef ElastixComponent_hxx +#define ElastixComponent_hxx + +namespace selx { + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::ElastixFilter( void ) +{ + this->AddRequiredInputName( "FixedImage" ); + this->AddRequiredInputName( "MovingImage" ); + this->AddRequiredInputName( "ParameterObject"); + + this->SetPrimaryInputName( "FixedImage" ); + this->SetPrimaryOutputName( "ResultImage" ); + + this->m_FixedImageContainer = DataObjectContainerType::New(); + this->m_MovingImageContainer = DataObjectContainerType::New(); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::GenerateData( void ) +{ + ElastixMainObjectPointer transform = 0; + DataObjectContainerPointer fixedImageContainer = this->m_FixedImageContainer; + DataObjectContainerPointer movingImageContainer = this->m_MovingImageContainer; + DataObjectContainerPointer fixedMaskContainer = 0; + DataObjectContainerPointer movingMaskContainer = 0; + DataObjectContainerPointer resultImageContainer = 0; + + ParameterMapListType TransformParameterMapList; + + // Fixed mask (optional) + if( this->HasInput( "FixedMask" ) ) + { + fixedMaskContainer = DataObjectContainerType::New(); + fixedMaskContainer->CreateElementAt( 0 ) = this->GetInput( "FixedMask" ); + } + + // Moving mask (optional) + if( this->HasInput( "MovingMask" ) ) + { + movingMaskContainer = DataObjectContainerType::New(); + movingMaskContainer->CreateElementAt( 0 ) = this->GetInput( "MovingMask" ); + } + + // Get ParameterMap + ParameterObjectPointer parameterObject = static_cast< ParameterObject* >( this->GetInput( "ParameterObject" ) ); + ParameterMapListType parameterMapList = parameterObject->GetParameterMapList(); + + // Emulate command line parameters. There must be an "-out", this is checked later in code + ArgumentMapType argumentMap; + argumentMap.insert( ArgumentMapEntryType( std::string( "-out" ), std::string( "output_path_not_set" ) ) ); + + // Direction Cosines + FlatDirectionCosinesType fixedImageOriginalDirection; + + // Setup xout + if( elx::xoutSetup( this->GetLogToFile().c_str(), this->GetLogToFile() != std::string(), this->GetLogToConsole() ) ) + { + itkExceptionMacro( "ERROR while setting up xout." ); + } + + // Run the (possibly multiple) registration(s) + unsigned int isError; + for( unsigned int i = 0; i < parameterMapList.size(); ++i ) + { + // Create another instance of ElastixMain + ElastixMainPointer elastix = ElastixMainType::New(); + + // Set stuff we get from a previous registration + elastix->SetInitialTransform( transform ); + elastix->SetFixedImageContainer( fixedImageContainer ); + elastix->SetMovingImageContainer( movingImageContainer ); + elastix->SetFixedMaskContainer( fixedMaskContainer ); + elastix->SetMovingMaskContainer( movingMaskContainer ); + elastix->SetResultImageContainer( resultImageContainer ); + elastix->SetOriginalFixedImageDirectionFlat( fixedImageOriginalDirection ); + + // Set the current elastix-level + elastix->SetElastixLevel( i ); + elastix->SetTotalNumberOfElastixLevels( 1 ); + + // ITK pipe-line mechanism need a result image + parameterMapList[ i ][ "WriteResultImage"] = ParameterValuesType( 1, std::string( "true" ) ); + + // Start registration + isError = elastix->Run( argumentMap, parameterMapList[ i ] ); + + // Assert success + if( isError != 0 ) + { + itkExceptionMacro( "Errors occured" ); + } + + // Get the transform, the fixedImage and the movingImage + // in order to put it in the next registration. + transform = elastix->GetFinalTransform(); + fixedImageContainer = elastix->GetFixedImageContainer(); + movingImageContainer = elastix->GetMovingImageContainer(); + fixedMaskContainer = elastix->GetFixedMaskContainer(); + movingMaskContainer = elastix->GetMovingMaskContainer(); + resultImageContainer = elastix->GetResultImageContainer(); + fixedImageOriginalDirection = elastix->GetOriginalFixedImageDirectionFlat(); + + TransformParameterMapList.push_back( elastix->GetTransformParametersMap() ); + + // Set initial transform to an index number instead of a parameter filename + if( i > 0 ) + { + std::stringstream index; + index << ( i - 1 ); + TransformParameterMapList[ i ][ "InitialTransformParametersFileName" ][ 0 ] = index.str(); + } + } // End loop over registrations + + // Save result image + if( resultImageContainer.IsNotNull() && resultImageContainer->Size() > 0 ) + { + this->SetOutput( "ResultImage", resultImageContainer->ElementAt( 0 ) ); + } + + // Save parameter map + ParameterObject::Pointer TransformParameters = ParameterObject::New(); + TransformParameters->SetParameterMapList( TransformParameterMapList ); + this->SetOutput( "TransformParametersObject", static_cast< itk::DataObject* >( TransformParameters ) ); + + // Clean up + transform = 0; + fixedImageContainer = 0; + movingImageContainer = 0; + fixedMaskContainer = 0; + movingMaskContainer = 0; + resultImageContainer = 0; + + // Close the modules + ElastixMainType::UnloadComponents(); + +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetFixedImage( FixedImagePointer fixedImage ) +{ + if( this->m_FixedImageContainer->Size() > 0 ) + { + // Free images that has already been given + this->m_FixedImageContainer = 0; + } + + // Input for elastix + this->m_FixedImageContainer->CreateElementAt( 0 ) = static_cast< itk::DataObject* >( fixedImage ) ; + + // Primary input for ITK pipeline + this->SetInput( DataObjectIdentifierType( "FixedImage" ), this->m_FixedImageContainer->ElementAt( 0 ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetFixedImage( DataObjectContainerPointer fixedImages ) +{ + // Input for elastix + this->m_FixedImageContainer = fixedImages; + + // Primary input for ITK pipeline + this->SetInput( DataObjectIdentifierType( "FixedImage" ), this->m_FixedImageContainer->ElementAt( 0 ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetMovingImage( MovingImagePointer movingImage ) +{ + if( this->m_MovingImageContainer->Size() > 0 ) + { + // Free images that has already been given + this->m_MovingImageContainer = 0; + } + + // Input for elastix + this->m_MovingImageContainer->CreateElementAt( 0 ) = static_cast< itk::DataObject* >( movingImage ) ; + + // Primary input for ITK pipeline + this->SetInput( DataObjectIdentifierType( "MovingImage" ), this->m_MovingImageContainer->ElementAt( 0 ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetMovingImage( DataObjectContainerPointer movingImages ) +{ + // Input for elastix + this->m_MovingImageContainer = movingImages; + + // Primary input for ITK pipeline + this->SetInput( DataObjectIdentifierType( "MovingImage" ), this->m_MovingImageContainer->ElementAt( 0 ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetParameterObject( ParameterObjectPointer parameterObject ) +{ + this->SetInput( DataObjectIdentifierType( "ParameterObject" ), static_cast< itk::DataObject* >( parameterObject ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetFixedMask( FixedImagePointer fixedMask ) +{ + this->SetInput( DataObjectIdentifierType( "FixedMask" ), static_cast< itk::DataObject* >( fixedMask ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +void +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::SetMovingMask( MovingImagePointer movingMask ) +{ + this->SetInput( DataObjectIdentifierType( "MovingMask" ), static_cast< itk::DataObject* >( movingMask ) ); +} + +template< typename TFixedImage, typename TMovingImage, typename TOutputImage > +typename selx::ElastixFilter< TFixedImage, TMovingImage, TOutputImage >::ParameterObjectPointer +ElastixFilter< TFixedImage, TMovingImage, TOutputImage > +::GetTransformParameters( void ) +{ + return static_cast< ParameterObject* >( itk::ProcessObject::GetOutput( DataObjectIdentifierType( "TransformParametersObject" ) ) ); +} + +} // namespace selx + +#endif // ElastixComponent_hxx \ No newline at end of file diff --git a/Modules/Core/Blueprints/include/elxBlueprint.h b/Modules/Core/Blueprints/include/elxBlueprint.h index f0a9d19df23ee68bb071f4b27439226cc546eff0..5a195885929d40855c6d9bcbdba2b4267c463aeb 100644 --- a/Modules/Core/Blueprints/include/elxBlueprint.h +++ b/Modules/Core/Blueprints/include/elxBlueprint.h @@ -1,5 +1,5 @@ -#ifndef __Blueprint_h -#define __Blueprint_h +#ifndef Blueprint_h +#define Blueprint_h #include "boost/graph/graph_traits.hpp" #include "boost/graph/directed_graph.hpp" @@ -9,7 +9,7 @@ #include "elxMacro.h" -namespace elx { +namespace selx { class Blueprint : public itk::DataObject { @@ -28,7 +28,7 @@ public: }; // Component parameter map that sits on an edge in the graph - // and holds component configuration settings + // and holds component connection configuration settings struct ConnectionPropertyType { ParameterMapType parameterMap; }; @@ -103,4 +103,4 @@ private: } -#endif // #define __Blueprint_h \ No newline at end of file +#endif // #define Blueprint_h \ No newline at end of file diff --git a/Modules/Core/Blueprints/src/elxBlueprint.cxx b/Modules/Core/Blueprints/src/elxBlueprint.cxx index d78d2ce6882287d6a42176310b6afea3c2cb0ff4..d8b667478c64cceb5d053d5498ffed316770ece7 100644 --- a/Modules/Core/Blueprints/src/elxBlueprint.cxx +++ b/Modules/Core/Blueprints/src/elxBlueprint.cxx @@ -1,11 +1,11 @@ -#ifndef __Blueprint_cxx -#define __Blueprint_cxx +#ifndef Blueprint_cxx +#define Blueprint_cxx #include "boost/graph/graphviz.hpp" #include "elxBlueprint.h" -namespace elx { +namespace selx { Blueprint::ComponentIndexType Blueprint @@ -155,6 +155,6 @@ Blueprint boost::write_graphviz( dotfile, this->m_Graph ); } -} // namespace elx +} // namespace selx -#endif // __Blueprint_cxx +#endif // Blueprint_cxx diff --git a/Modules/Core/ComponentInterface/include/ComponentSelector.h b/Modules/Core/ComponentInterface/include/ComponentSelector.h index b4e325936b73503b67747bc84f42890078f0b0e8..f4cae7d060d452fdde24bd154640631bf6f0eeae 100644 --- a/Modules/Core/ComponentInterface/include/ComponentSelector.h +++ b/Modules/Core/ComponentInterface/include/ComponentSelector.h @@ -50,7 +50,7 @@ public: typedef ComponentBase::Pointer ComponentBasePointer; typedef ComponentBase::CriteriaType CriteriaType; - typedef std::list< typename ComponentBasePointer > ComponentListType; + typedef std::list< ComponentBasePointer > ComponentListType; /** set selection criteria for possibleComponents*/ void Initialize(); void SetCriteria(const CriteriaType &criteria); diff --git a/Modules/Core/elxModuleCore.cmake b/Modules/Core/ModuleCore.cmake similarity index 91% rename from Modules/Core/elxModuleCore.cmake rename to Modules/Core/ModuleCore.cmake index 3edca54fc96186c06aad34d20e91eba094558600..b57854f03f07ffefa37fde7fd7c34c2aba27b74a 100644 --- a/Modules/Core/elxModuleCore.cmake +++ b/Modules/Core/ModuleCore.cmake @@ -1,9 +1,15 @@ -set( MODULE elxModuleCore ) +set( MODULE ModuleCore ) + +# Module source files +set( ${MODULE}_SOURCE_FILES + ${${MODULE}_SOURCE_DIR}/Blueprints/src/elxBlueprint.cxx +) # Export include files set( ${MODULE}_INCLUDE_DIRS ${${MODULE}_SOURCE_DIR}/Common/include ${${MODULE}_SOURCE_DIR}/Blueprints/include + ${${MODULE}_SOURCE_DIR}/ParameterObject/include ${${MODULE}_SOURCE_DIR}/ComponentInterface/include ${${MODULE}_SOURCE_DIR}/ExampleComponents/include ) @@ -35,8 +41,8 @@ set( ${MODULE}_SOURCE_FILES ${${MODULE}_SOURCE_DIR}/ExampleComponents/src/MetricComponent1.cxx ) - # Compile library add_library( ${MODULE} STATIC "${${MODULE}_SOURCE_FILES}" ${${MODULE}_HEADER_FILES}) + target_link_libraries( ${MODULE} ${ELASTIX_LIBRARIES} ) diff --git a/Modules/Core/ParameterObject/include/elxParameterObject.h b/Modules/Core/ParameterObject/include/elxParameterObject.h new file mode 100644 index 0000000000000000000000000000000000000000..3bfbe1f49cbd15f614cd49c7daa0adb7e698701e --- /dev/null +++ b/Modules/Core/ParameterObject/include/elxParameterObject.h @@ -0,0 +1,56 @@ +#ifndef ParameterObject_h +#define ParameterObject_h + +#include "itkObjectFactory.h" +#include "itkDataObject.h" + +#include "elxMacro.h" + +namespace selx { + +class ParameterObject : public itk::DataObject +{ +public: + + elxNewMacro( ParameterObject, itk::DataObject ); + + typedef std::vector< std::string > ParameterValuesType; + typedef std::map< std::string, ParameterValuesType > ParameterMapType; + typedef std::vector< ParameterMapType > ParameterMapListType; + + void SetParameterMap( ParameterMapType parameterMap ) + { + ParameterMapListType parameterMapList; + parameterMapList.push_back( parameterMap ); + this->SetParameterMapList( parameterMapList ); + } + + void SetParameterMapList( ParameterMapListType parameterMapList ) + { + this->m_ParameterMapList = parameterMapList; + }; + + ParameterMapListType GetParameterMapList( void ) + { + return this->m_ParameterMapList; + }; + + // TODO: + // itkSetMacro( ParameterMap, ParameterMapType ) + // itkGetMacro( ParameterMap, ParameterMapType ) + + // friend ITKCommon_EXPORT std::ostream& operator<<( std::ostream& os, const ParameterObject& parameterObject ) + // { + // os << parameterObject.m_ParameterMapList; + // return os; + // } + +private: + + ParameterMapListType m_ParameterMapList; + +}; + +} // namespace elx + +#endif // #define ParameterMap_h \ No newline at end of file diff --git a/SuperBuild/CMakeLists.txt b/SuperBuild/CMakeLists.txt index 42f3f9f5df469a6cd04b17183168b264ac363433..0b216a8174184a8c62d742f48b41f3cb0824b267 100644 --- a/SuperBuild/CMakeLists.txt +++ b/SuperBuild/CMakeLists.txt @@ -70,8 +70,9 @@ endif() # --------------------------------------------------------------------- # Build GoogleTest +mark_as_advanced( USE_SYSTEM_GOOGLETEST ) option( USE_SYSTEM_GOOGLETEST "Use a pre-compiled version of GoogleTest. " OFF ) -mark_as_advanced(USE_SYSTEM_GOOGLETEST) +mark_as_advanced( USE_SYSTEM_GOOGLETEST ) if ( SUPERELASTIX_BUILD_TESTING ) if ( USE_SYSTEM_GOOGLETEST ) find_package( GTest REQUIRED ) @@ -83,11 +84,19 @@ endif() # --------------------------------------------------------------------- # Build Elastix -# TODO: Add USE_SYSTEM_ELASTICLEGACY option mark_as_advanced( SUPERELASTIX_BUILD_ELASTIX ) option( SUPERELASTIX_BUILD_ELASTIX ON ) if( SUPERELASTIX_BUILD_ELASTIX ) - include( ExternalElastix ) + if( USE_SYSTEM_ELASTIX ) + if( NOT EXISTS ${ELASTIX_USE_FILE} ) + # Expose CMake variable to user + set( ELASTIX_USE_FILE ) + # Stop the build + message( FATAL_ERROR "Please point ELASTIX_USE_FILE to your systems UseElastix.cmake file." ) + endif() + else() + include( ExternalElastix ) + endif() endif() # --------------------------------------------------------------------- diff --git a/SuperBuild/ExternalElastix.cmake b/SuperBuild/ExternalElastix.cmake index f99860c9de6e3a144909a695553ef200055ec313..c1fe6b59df0d56977f82eead1b340b63d644d1ff 100644 --- a/SuperBuild/ExternalElastix.cmake +++ b/SuperBuild/ExternalElastix.cmake @@ -1,7 +1,7 @@ set( proj Elastix ) set( ELASTIX_REPOSITORY git://github.com/mstaring/elastix.git ) -set( ELASTIX_TAG 6eb6fc7e0d86bafdfdffb1b8bbd125f2a56a486e ) +set( ELASTIX_TAG c35842cd0152d8fd2894e7c6706d2dd8396f0fed ) ExternalProject_Add( ${proj} GIT_REPOSITORY ${ELASTIX_REPOSITORY} diff --git a/Testing/CMakeLists.txt b/Testing/CMakeLists.txt index 71f3fea9c73a24d50f491a6825edcaeadc57ac8d..0d4ef1b6ad883fe7ac3125eb6a2a9367815a38ce 100644 --- a/Testing/CMakeLists.txt +++ b/Testing/CMakeLists.txt @@ -17,17 +17,17 @@ list( APPEND ExternalData_URL_TEMPLATES # The content links contains md5 hashes that are checked for consistensy # against files downloaded from servers in ExternalData_URL_TEMPLATES. These # files are placed in a corresponding location in the build directory. -file( GLOB_RECURSE ElastixDataContentLinks "*.md5" ) -foreach(ElastixDataContentLink ${ElastixDataContentLinks}) - string( REGEX REPLACE "\\.md5$" "" ElastixDataContentLink ${ElastixDataContentLink} ) - ExternalData_Expand_Arguments( ElastixData - ElastixDataFilenames - DATA{${ElastixDataContentLink}} +file( GLOB_RECURSE SuperElastixDataContentLinks "*.md5" ) +foreach( SuperElastixDataContentLink ${SuperElastixDataContentLinks}) + string( REGEX REPLACE "\\.md5$" "" SuperElastixDataContentLink ${SuperElastixDataContentLink} ) + ExternalData_Expand_Arguments( SuperElastixData + SuperElastixDataFilenames + DATA{${SuperElastixDataContentLink}} ) endforeach() # Test data is downloaded when this target is built -ExternalData_Add_Target( ElastixData ) +ExternalData_Add_Target( SuperElastixData ) # --------------------------------------------------------------------- # Find GoogleTest library @@ -49,9 +49,9 @@ add_subdirectory( Unit ) # --------------------------------------------------------------------- # Configure dashboard -mark_as_advanced( ELASTIX_BUILD_DASHBOARD ) -option( ELASTIX_BUILD_DASHBOARD "Enable elastix dashboard." OFF ) +mark_as_advanced( SUPERELASTIX_BUILD_DASHBOARD ) +option( SUPERELASTIX_BUILD_DASHBOARD "Enable SuperElastix dashboard." OFF ) -if( ${ELASTIX_BUILD_DASHBOARD} ) +if( ${SUPERELASTIX_BUILD_DASHBOARD} ) add_subdirectory( Dashboard ) endif() diff --git a/Testing/Unit/CMakeLists.txt b/Testing/Unit/CMakeLists.txt index 2186fe7715fd5279d4dcc6a14c2f4ae0c6d06086..6270de443aa16e688de9ea1d79e562e6ce98df6f 100644 --- a/Testing/Unit/CMakeLists.txt +++ b/Testing/Unit/CMakeLists.txt @@ -4,8 +4,8 @@ # the elastix dashboard. set( ElastixUnitTestFilenames - elxExampleUnitTest.cxx elxBlueprintTest.cxx + elxElastixFilterTest.cxx elxComponentFactoryTest.cxx elxComponentInterfaceTest.cxx ) @@ -13,15 +13,15 @@ set( ElastixUnitTestFilenames # --------------------------------------------------------------------- # Set data directories -set( ELASTIX_UNITTEST_INPUT_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/Data/Input ) -set( ELASTIX_UNITTEST_OUTPUT_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/Data/Output ) -set( ELASTIX_UNITTEST_BASELINE_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/Data/Baseline ) +set( SUPERELASTIX_UNITTEST_INPUT_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/Data/Input ) +set( SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/Data/Output ) +set( SUPERELASTIX_UNITTEST_BASELINE_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/Data/Baseline ) -if( NOT EXISTS ${ELASTIX_UNITTEST_OUTPUT_DATA_DIR} ) - file( MAKE_DIRECTORY ${ELASTIX_UNITTEST_OUTPUT_DATA_DIR} ) +if( NOT EXISTS ${SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR} ) + file( MAKE_DIRECTORY ${SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR} ) endif() -if( NOT EXISTS ${ELASTIX_UNITTEST_OUTPUT_DATA_DIR} ) +if( NOT EXISTS ${SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR} ) message( FATAL_ERROR "Could not create directory for output data. Make sure elastix has permission to write to disk." ) @@ -48,7 +48,7 @@ foreach( ElastixUnitTestFilename ${ElastixUnitTestFilenames} ) # Build tests executables string( REPLACE ".cxx" "" ElastixUnitTest ${ElastixUnitTestFilename} ) add_executable( ${ElastixUnitTest} ${ElastixUnitTestFilename} ) - target_link_libraries( "${ElastixUnitTest}" ${ELASTIX_LIBRARIES} ${ITK_LIBRARIES} ${TEST_LIBRARIES} ) + target_link_libraries( "${ElastixUnitTest}" ${SUPERELASTIX_LIBRARIES} ${ITK_LIBRARIES} ${TEST_LIBRARIES} ) # Add GoogleTest to CTest GTEST_ADD_TESTS( ${ElastixUnitTest} "--gtest_output=xml:${CMAKE_BINARY_DIR}/Testing/Unit/${ElastixUnitTest}.xml" ${ElastixUnitTestFilename} ) diff --git a/Testing/Unit/Data/Input/cthead1-Float.mha.md5 b/Testing/Unit/Data/Input/cthead1-Float.mha.md5 new file mode 100644 index 0000000000000000000000000000000000000000..815300480e124b9a3eca98655a895642354483c2 --- /dev/null +++ b/Testing/Unit/Data/Input/cthead1-Float.mha.md5 @@ -0,0 +1 @@ +25de5707b18c0c684fd5fa30351bf787 \ No newline at end of file diff --git a/Testing/Unit/elxBluePrintTest.cxx b/Testing/Unit/elxBlueprintTest.cxx similarity index 96% rename from Testing/Unit/elxBluePrintTest.cxx rename to Testing/Unit/elxBlueprintTest.cxx index 15ffde06c38d85233944c28cf7bb837597e2f651..1f838fbb5999352d1812e34c4a8eaf67c5c5178a 100644 --- a/Testing/Unit/elxBluePrintTest.cxx +++ b/Testing/Unit/elxBlueprintTest.cxx @@ -2,7 +2,7 @@ #include "gtest/gtest.h" -namespace elx { +using namespace selx; class BlueprintTest : public ::testing::Test { public: @@ -53,7 +53,7 @@ TEST_F( BlueprintTest, SetComponent ) } // TODO: The final line segfaults because GetComponent does not check that the index actually -// actually exist. How can we do that? See also explanation in elxBlueprint.h +// actually exist. See explanation in elxBlueprint.h // TEST_F( BlueprintTest, DeleteComponent ) // { // BlueprintPointerType blueprint = Blueprint::New(); @@ -98,9 +98,6 @@ TEST_F( BlueprintTest, AddConnection ) // of the next test. ParameterMapType parameterMapTest1; EXPECT_TRUE( blueprint->AddConnection( index1, index2, parameterMap ) ); - - // It is not be possible to add connection between components that do not exist - // because you do not have necessary indexes } TEST_F( BlueprintTest, GetConnection ) @@ -170,5 +167,3 @@ TEST_F( BlueprintTest, WriteBlueprint ) EXPECT_NO_THROW( blueprint->WriteBlueprint( "blueprint.dot" ) ); } - -} // namespace elx diff --git a/Testing/Unit/elxDataDirectories.h.in b/Testing/Unit/elxDataDirectories.h.in index 2081964057c33d7647da456782aff3e956135efd..9ee3ae725f543a5b8fcde158f869b7496e32139e 100644 --- a/Testing/Unit/elxDataDirectories.h.in +++ b/Testing/Unit/elxDataDirectories.h.in @@ -1,8 +1,8 @@ #ifndef __elxDataDirectories_h #define __elxDataDirectories_h -#define ELASTIX_UNITTEST_INPUT_DATA_DIR "@ELASTIX_UNITTEST_INPUT_DATA_DIR@" -#define ELASTIX_UNITTEST_OUTPUT_DATA_DIR "@ELASTIX_UNITTEST_OUTPUT_DATA_DIR@" -#define ELASTIX_UNITTEST_BASELINE_DATA_DIR "@ELASTIX_UNITTEST_BASELINE_DATA_DIR@" +#define SUPERELASTIX_UNITTEST_INPUT_DATA_DIR "@SUPERELASTIX_UNITTEST_INPUT_DATA_DIR@" +#define SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR "@SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR@" +#define SUPERELASTIX_UNITTEST_BASELINE_DATA_DIR "@SUPERELASTIX_UNITTEST_BASELINE_DATA_DIR@" #endif // __elxTestDataDirectories_h \ No newline at end of file diff --git a/Testing/Unit/elxDataManager.cxx b/Testing/Unit/elxDataManager.cxx index 6b7da8aa318a592f9ff654404d5412f93d829dd5..e9c41d69803246304c8b5bff70af086dc8a75cac 100644 --- a/Testing/Unit/elxDataManager.cxx +++ b/Testing/Unit/elxDataManager.cxx @@ -6,7 +6,7 @@ const std::string DataManager -::GetInputFullPath( const std::string filename ) const +::GetInputFile( const std::string filename ) const { const std::string path = this->GetInputDirectory() + this->GetFolderSeparator() + filename; return path; @@ -14,7 +14,7 @@ DataManager const std::string DataManager -::GetOutputFullPath( const std::string filename ) const +::GetOutputFile( const std::string filename ) const { const std::string path = this->GetOutputDirectory() + this->GetFolderSeparator() + filename; return path; @@ -22,10 +22,10 @@ DataManager const std::string DataManager -::GetBaselineFullPath( const std::string filename ) const +::GetBaselineFile( const std::string filename ) const { const std::string path = this->GetBaselineDirectory() + this->GetFolderSeparator() + filename; return path; } -#endif // __elxTestData_cxx \ No newline at end of file +#endif // __DataManager_cxx \ No newline at end of file diff --git a/Testing/Unit/elxDataManager.h b/Testing/Unit/elxDataManager.h index fc85a65c4a30c00d979a10aa6b7a464ba2481937..3d15d3861b0db7f31e4febabd3ae2ab7aca1db30 100644 --- a/Testing/Unit/elxDataManager.h +++ b/Testing/Unit/elxDataManager.h @@ -17,18 +17,18 @@ public: DataManager() { - this->m_InputDirectory = ELASTIX_UNITTEST_INPUT_DATA_DIR; - this->m_OutputDirectory = ELASTIX_UNITTEST_OUTPUT_DATA_DIR; - this->m_BaselineDirectory = ELASTIX_UNITTEST_BASELINE_DATA_DIR; + this->m_InputDirectory = SUPERELASTIX_UNITTEST_INPUT_DATA_DIR; + this->m_OutputDirectory = SUPERELASTIX_UNITTEST_OUTPUT_DATA_DIR; + this->m_BaselineDirectory = SUPERELASTIX_UNITTEST_BASELINE_DATA_DIR; } std::string GetInputDirectory( void ) const { return this->m_InputDirectory; }; std::string GetOutputDirectory( void ) const { return this->m_OutputDirectory; }; std::string GetBaselineDirectory( void ) const { return this->m_BaselineDirectory; }; - const std::string GetInputFullPath( const std::string filename ) const; - const std::string GetOutputFullPath( const std::string filename ) const; - const std::string GetBaselineFullPath( const std::string filename ) const; + const std::string GetInputFile( const std::string filename ) const; + const std::string GetOutputFile( const std::string filename ) const; + const std::string GetBaselineFile( const std::string filename ) const; std::string GetFolderSeparator() const { @@ -56,4 +56,4 @@ private: }; -#endif // #define __DataManager_h \ No newline at end of file +#endif // __DataManager_h \ No newline at end of file diff --git a/Testing/Unit/elxElastixFilterTest.cxx b/Testing/Unit/elxElastixFilterTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6dac45f0457d4ea648045eefedbf9c59e265a677 --- /dev/null +++ b/Testing/Unit/elxElastixFilterTest.cxx @@ -0,0 +1,155 @@ +#include "elxElastixFilter.h" +#include "elxParameterObject.h" + +#include "itkImageFileReader.h" +#include "itkImageFileWriter.h" + +#include "elxDataManager.h" +#include "gtest/gtest.h" + +using namespace selx; + +class ElastixFilterTest : public ::testing::Test +{ +protected: + + typedef DataManager DataManagerType; + + typedef itk::Image< float, 2u > ImageType; + ImageType::Pointer fixedImage; + ImageType::Pointer movingImage; + ImageType::Pointer resultImage; + + typedef itk::ImageFileReader< ImageType > ImageReaderType; + typedef itk::ImageFileWriter< ImageType > ImageWriterType; + + typedef ParameterObject::ParameterValuesType ParameterValuesType; + typedef ParameterObject::ParameterMapType ParameterMapType; + ParameterObject::Pointer parameterObject; + ParameterObject::Pointer TransformParameterObject; + + typedef ElastixFilter< ImageType, ImageType, ImageType > ElastixFilterType; + typedef ElastixFilterType::DataObjectContainerType DataObjectContainerType; + typedef ElastixFilterType::DataObjectContainerPointer DataObjectContainerPointer; + + + virtual void SetUp() + { + DataManagerType::Pointer dataManager = DataManagerType::New(); + + ImageReaderType::Pointer reader = ImageReaderType::New(); + + reader->SetFileName( dataManager->GetInputFile( "cthead1-Float.mha" ) ); + fixedImage = reader->GetOutput(); + + // TODO: Only call update on writer + reader->Update(); + + reader->SetFileName( dataManager->GetInputFile( "cthead1-Float.mha" ) ); + movingImage = reader->GetOutput(); + + // TODO: Only call update on writer + reader->Update(); + + // ParameterMap + ParameterMapType parameterMap = ParameterMapType(); + + // Images + parameterMap[ "FixedInternalImagePixelType" ] = ParameterValuesType( 1, "float" ); + parameterMap[ "FixedImageDimension" ] = ParameterValuesType( 1, "2" ); + parameterMap[ "MovingInternalImagePixelType" ] = ParameterValuesType( 1, "float" ); + parameterMap[ "MovingImageDimension" ] = ParameterValuesType( 1, "2" ); + parameterMap[ "ResultImagePixelType" ] = ParameterValuesType( 1, "float" ); + + // Common components + parameterMap[ "FixedImagePyramid" ] = ParameterValuesType( 1, "FixedSmoothingImagePyramid" ); + parameterMap[ "MovingImagePyramid" ] = ParameterValuesType( 1, "MovingSmoothingImagePyramid" ); + parameterMap[ "Interpolator"] = ParameterValuesType( 1, "LinearInterpolator"); + parameterMap[ "Optimizer" ] = ParameterValuesType( 1, "AdaptiveStochasticGradientDescent" ); + parameterMap[ "Resampler"] = ParameterValuesType( 1, "DefaultResampler" ); + parameterMap[ "ResampleInterpolator"] = ParameterValuesType( 1, "FinalLinearInterpolator" ); + parameterMap[ "FinalBSplineInterpolationOrder" ] = ParameterValuesType( 1, "2" ); + parameterMap[ "NumberOfResolutions" ] = ParameterValuesType( 1, "2" ); + + // Image Sampler + parameterMap[ "ImageSampler" ] = ParameterValuesType( 1, "RandomCoordinate" ); + parameterMap[ "NumberOfSpatialSamples"] = ParameterValuesType( 1, "2048" ); + parameterMap[ "CheckNumberOfSamples" ] = ParameterValuesType( 1, "true" ); + parameterMap[ "MaximumNumberOfSamplingAttempts" ] = ParameterValuesType( 1, "8" ); + parameterMap[ "NewSamplesEveryIteration" ] = ParameterValuesType( 1, "true"); + + // Optimizer + parameterMap[ "NumberOfSamplesForExactGradient" ] = ParameterValuesType( 1, "4096" ); + parameterMap[ "DefaultPixelValue" ] = ParameterValuesType( 1, "0" ); + parameterMap[ "AutomaticParameterEstimation" ] = ParameterValuesType( 1, "true" ); + + // Output + parameterMap[ "WriteResultImage" ] = ParameterValuesType( 1, "false" ); + parameterMap[ "ResultImageFormat" ] = ParameterValuesType( 1, "nii" ); + + // Registration + parameterMap[ "Registration" ] = ParameterValuesType( 1, "MultiResolutionRegistration" ); + parameterMap[ "Transform" ] = ParameterValuesType( 1, "EulerTransform" ); + parameterMap[ "Metric" ] = ParameterValuesType( 1, "AdvancedMattesMutualInformation" ); + parameterMap[ "MaximumNumberOfIterations" ] = ParameterValuesType( 1, "128" ); + + parameterObject = ParameterObject::New(); + parameterObject->SetParameterMap( parameterMap ); + } +}; + +TEST_F( ElastixFilterTest, Instantiation ) +{ + EXPECT_NO_THROW( ElastixFilterType::Pointer elastixFilter = ElastixFilterType::New() ); +} + +TEST_F( ElastixFilterTest, PairwiseRegistration ) +{ + ElastixFilterType::Pointer elastixFilter = ElastixFilterType::New(); + + elastixFilter->LogToConsoleOn(); + elastixFilter->SetFixedImage( fixedImage ); + elastixFilter->SetMovingImage( movingImage ); + elastixFilter->SetParameterObject( parameterObject ); + + // TODO: This update should not be needed + elastixFilter->Update(); + + EXPECT_NO_THROW( resultImage = elastixFilter->GetOutput() ); + EXPECT_NO_THROW( TransformParameterObject = elastixFilter->GetTransformParameters() ); + + ImageWriterType::Pointer writer = ImageWriterType::New(); + writer->SetFileName( "ElastixResultImage.nii" ); + writer->SetInput( elastixFilter->GetOutput() ); + EXPECT_NO_THROW( writer->Update() ); + +} + +TEST_F( ElastixFilterTest, MultiPairwiseRegistration ) +{ + ElastixFilterType::Pointer elastixFilter = ElastixFilterType::New(); + + DataObjectContainerPointer fixedImages = DataObjectContainerType::New(); + fixedImages->CreateElementAt( 0 ) = static_cast< itk::DataObject* >( fixedImage ); + fixedImages->CreateElementAt( 1 ) = static_cast< itk::DataObject* >( fixedImage ); + + DataObjectContainerPointer movingImages = DataObjectContainerType::New(); + movingImages->CreateElementAt( 0 ) = static_cast< itk::DataObject* >( movingImage ); + movingImages->CreateElementAt( 1 ) = static_cast< itk::DataObject* >( movingImage ); + + elastixFilter->LogToConsoleOn(); + elastixFilter->SetFixedImage( fixedImages ); + elastixFilter->SetMovingImage( movingImages ); + elastixFilter->SetParameterObject( parameterObject ); + + // TODO: This update should not be needed + elastixFilter->Update(); + + EXPECT_NO_THROW( resultImage = elastixFilter->GetOutput() ); + EXPECT_NO_THROW( TransformParameterObject = elastixFilter->GetTransformParameters() ); + + ImageWriterType::Pointer writer = ImageWriterType::New(); + writer->SetFileName( "ElastixResultImage.nii" ); + writer->SetInput( elastixFilter->GetOutput() ); + EXPECT_NO_THROW( writer->Update() ); +}