Unverified Commit 14c959ff authored by Floris Berendsen's avatar Floris Berendsen Committed by GitHub
Browse files

Merge pull request #157 from SuperElastix/SELX-143-Add-demo-registration-with-masks

Selx 143 add demo registration with masks
parents 7dc499e4 a34dbb11
......@@ -88,6 +88,7 @@ set( ConfigFiles
"elastix_Bspline_NC.json"
"elastix_Bspline_MSD.json"
"IdentityTransformRegistration.json"
"elastix3d.json"
)
foreach( ConfigFile ${ConfigFiles} )
......@@ -105,6 +106,7 @@ set( ScriptFiles
"2A_SuperElastix_itkv4_NC"
"2B_SuperElastix_itkv4_MSD"
"IdentityTransformRegistration"
"elastix3d"
"graphviz_to_png"
)
......
# data from Namic - Deformable registration speed optimization http://www.insight-journal.org/midas/collection/view/29
../SuperElastix --conf ../Configuration/elastix3d.json --loglevel debug --in FixedImage=N012_S03_tof3d_multi_slab.mha MovingImage=N026_S02_tof3d_multi_slab.mha --out ResultImage=elastix3d_warped.mhd ResultDisplacementField=elastix3d_displacement.mhd
REM data from Namic - Deformable registration speed optimization http://www.insight-journal.org/midas/collection/view/29
..\SuperElastix.exe --conf ..\Configuration\elastix3d.json --loglevel debug --in FixedImage=N012_S03_tof3d_multi_slab.mha MovingImage=N026_S02_tof3d_multi_slab.mha --out ResultImage=elastix3d_warped.mhd ResultDisplacementField=elastix3d_displacement.mhd
......@@ -40,7 +40,7 @@ set(CTEST_BUILD_NAME "${SELX_GIT_BRANCH_NAME};Tests;commit=SHA\\:${SELX_GIT_COMM
set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
set(CTEST_BUILD_CONFIGURATION Release)
set(CTEST_BUILD_FLAGS "-j4")
set(CTEST_BUILD_FLAGS "-j2")
set(CTEST_CONFIGURE_COMMAND "${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE:STRING=${CTEST_BUILD_CONFIGURATION} --build ${CTEST_BINARY_DIRECTORY}")
set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} -DWITH_TESTING:BOOL=ON ${CTEST_BUILD_OPTIONS}")
......
......@@ -29,6 +29,33 @@ struct PodString
static_assert( StaticErrorMessageRevealT< T >::False, "Please Implement PodString<T> for this T" );
};
template< >
struct PodString< bool >
{
static const char * Get()
{
return "bool";
}
};
template< >
struct PodString< unsigned char >
{
static const char * Get()
{
return "unsigned char";
}
};
template< >
struct PodString< char >
{
static const char * Get()
{
return "char";
}
};
template< >
struct PodString< unsigned int >
{
......
/*=========================================================================
*
* 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.
*
*=========================================================================*/
#ifndef selxElastixComponent_h
#define selxElastixComponent_h
#include "selxSuperElastixComponent.h"
#include "selxSinksAndSourcesInterfaces.h"
#include "selxItkObjectInterfaces.h"
#include "itkImageSource.h"
#include "elxElastixFilter.h"
#include "elxParameterObject.h"
#include "elxTransformixFilter.h"
#include <string.h>
namespace selx
{
template< int Dimensionality, class TPixel >
class ElastixComponent :
public SuperElastixComponent<
Accepting<
itkImageFixedInterface< Dimensionality, TPixel >,
itkImageMovingInterface< Dimensionality, TPixel >
>,
Providing<
itkImageInterface< Dimensionality, TPixel >,
UpdateInterface
>
>
{
public:
/** Standard ITK typedefs. */
typedef ElastixComponent<
Dimensionality, TPixel
> Self;
typedef SuperElastixComponent<
Accepting<
itkImageFixedInterface< Dimensionality, TPixel >,
itkImageMovingInterface< Dimensionality, TPixel >
>,
Providing<
itkImageInterface< Dimensionality, TPixel >,
UpdateInterface
>
> Superclass;
typedef std::shared_ptr< Self > Pointer;
typedef std::shared_ptr< const Self > ConstPointer;
ElastixComponent( const std::string & name, LoggerImpl & logger );
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 typename ConnectionImageType::Pointer ItkImagePointer;
typedef elastix::ElastixFilter< FixedImageType, MovingImageType > ElastixFilterType;
typedef elastix::ParameterObject elxParameterObjectType;
typedef elxParameterObjectType::Pointer elxParameterObjectPointer;
typedef elastix::TransformixFilter< FixedImageType > TransformixFilterType;
virtual int Accept( typename itkImageFixedInterface< Dimensionality, TPixel >::Pointer ) override;
virtual int Accept( typename itkImageMovingInterface< Dimensionality, TPixel >::Pointer ) override;
virtual ItkImagePointer GetItkImage() override;
virtual void Update() override;
virtual bool MeetsCriterion( const CriterionType & criterion ) override;
static const char * GetDescription() { return "Elastix Component"; }
private:
typename ElastixFilterType::Pointer m_elastixFilter;
typename TransformixFilterType::Pointer m_transformixFilter;
//selxParameterObjectPointer m_ParameterObject;
ItkImagePointer m_OutputImage;
protected:
// return the class name and the template arguments to uniquely identify this component.
static inline const std::map< std::string, std::string > TemplateProperties()
{
return { { keys::NameOfClass, "ElastixComponent" }, { keys::PixelType, PodString< TPixel >::Get() }, { keys::Dimensionality, std::to_string( Dimensionality ) } };
}
};
} //end namespace selx
#ifndef ITK_MANUAL_INSTANTIATION
#include "selxElastixComponent.hxx"
#endif
#endif // #define GDOptimizer3rdPartyComponent_h
/*=========================================================================
*
* 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.
*
*=========================================================================*/
#include "selxElastixComponent.h"
#include "selxCheckTemplateProperties.h"
namespace selx
{
template< int Dimensionality, class TPixel >
ElastixComponent< Dimensionality, TPixel >::ElastixComponent( const std::string & name, LoggerImpl & logger ) : Superclass( name, logger )
{
m_elastixFilter = ElastixFilterType::New();
m_transformixFilter = TransformixFilterType::New();
// TODO: Due to some issues with Criteria being propagated as elastix settings, I need to empty the selxparameterObject.
elxParameterObjectPointer elxParameterObject = elxParameterObjectType::New();
typename elxParameterObjectType::ParameterMapType defaultParameters = elxParameterObject->GetDefaultParameterMap( "rigid" );
elxParameterObject->SetParameterMap( defaultParameters );
m_elastixFilter->SetParameterObject( elxParameterObject );
m_elastixFilter->LogToConsoleOn();
m_elastixFilter->LogToFileOff();
m_elastixFilter->SetOutputDirectory( "." );
m_transformixFilter->ComputeDeformationFieldOn();
m_transformixFilter->LogToConsoleOn();
m_transformixFilter->LogToFileOff();
m_transformixFilter->SetOutputDirectory( "." );
//TODO m_elastixFilter returns a nullptr GetTransformParameterObject instead of a valid object. However, we need this object to satisfy the input conditions of m_transformixFilter
//m_transformixFilter->SetTransformParameterObject(m_elastixFilter->GetTransformParameterObject());
m_transformixFilter->SetTransformParameterObject( elxParameterObject ); // supply a dummy object
//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 >::Accept( typename itkImageFixedInterface< Dimensionality, TPixel >::Pointer component )
{
auto fixedImage = component->GetItkImageFixed();
// connect the itk pipeline
this->m_elastixFilter->SetFixedImage( fixedImage );
return 0;
}
template< int Dimensionality, class TPixel >
int
ElastixComponent< Dimensionality, TPixel >::Accept( typename itkImageMovingInterface< Dimensionality, TPixel >::Pointer component )
{
auto movingImage = component->GetItkImageMoving();
// connect the itk pipeline
this->m_elastixFilter->SetMovingImage( movingImage );
// In the current transformix filter an input image is required even if we want a deformation field only.
this->m_transformixFilter->SetMovingImage( movingImage );
return 0;
}
//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()
{
return this->m_transformixFilter->GetOutput();
}
template< int Dimensionality, class TPixel >
void
ElastixComponent< Dimensionality, TPixel >::Update( void )
{
// TODO currently, the pipeline with elastix and tranformix can only be created after the update of elastix
this->m_elastixFilter->Update();
this->m_transformixFilter->SetTransformParameterObject( this->m_elastixFilter->GetTransformParameterObject() );
this->m_transformixFilter->Update();
}
template< int Dimensionality, class TPixel >
bool
ElastixComponent< Dimensionality, TPixel >
::MeetsCriterion( const CriterionType & criterion )
{
bool hasUndefinedCriteria( false );
bool meetsCriteria( false );
auto status = CheckTemplateProperties( this->TemplateProperties(), criterion );
if( status == CriterionStatus::Satisfied )
{
return true;
}
else if( status == CriterionStatus::Failed )
{
return false;
} // else: CriterionStatus::Unknown
else if( criterion.first == "RegistrationPreset" ) //Supports this?
{
// Temporary solution: RegistrationPreset: rigid, nonrigid, etc overwrite the current selxparameterObject.
// Warning: the order of Criteria matters, since selxparameterObject 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 & presetName : criterion.second ) // auto&& preferred?
{
typename elxParameterObjectType::ParameterMapType presetParameters = elxParameterObject->GetDefaultParameterMap( presetName );
elxParameterObject->SetParameterMap( presetParameters );
try
{
this->m_elastixFilter->SetParameterObject( elxParameterObject );
}
catch( itk::ExceptionObject & err )
{
this->Error( err.what() );
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_elastixFilter->GetParameterObject();
typename elxParameterObjectType::ParameterMapType newParameterMap = elxParameterObject->GetParameterMap( 0 ); //copy const paramtermap to a non const map
newParameterMap[ criterion.first ] = criterion.second; //overwrite element
elxParameterObjectPointer newParameterObject = elxParameterObjectType::New();
newParameterObject->SetParameterMap( newParameterMap );
this->m_elastixFilter->SetParameterObject( newParameterObject );
meetsCriteria = true;
}
return meetsCriteria;
}
} //end namespace selx
......@@ -20,16 +20,16 @@
#include "selxTypeList.h"
//Component group Elastix
#include "selxElastixComponent.h"
#include "selxMonolithicElastixComponent.h"
#include "selxMonolithicTransformixComponent.h"
namespace selx
{
using ModuleElastixComponents = selx::TypeList<
ElastixComponent< 2, float >,
MonolithicElastixComponent< 2, float >,
MonolithicElastixComponent< 3, float >,
MonolithicElastixComponent< 3, short >,
MonolithicTransformixComponent< 2, float >
MonolithicTransformixComponent< 2, float >,
MonolithicTransformixComponent< 3, float >
>;
}
......@@ -38,7 +38,9 @@ class MonolithicElastixComponent :
public SuperElastixComponent<
Accepting<
itkImageFixedInterface< Dimensionality, TPixel >,
itkImageMovingInterface< Dimensionality, TPixel >
itkImageMovingInterface< Dimensionality, TPixel >,
itkImageFixedMaskInterface< Dimensionality, unsigned char >,
itkImageMovingMaskInterface< Dimensionality, unsigned char >
>,
Providing<
elastixTransformParameterObjectInterface< itk::Image< TPixel, Dimensionality >, itk::Image< TPixel, Dimensionality >>,
......@@ -56,7 +58,9 @@ public:
typedef SuperElastixComponent<
Accepting<
itkImageFixedInterface< Dimensionality, TPixel >,
itkImageMovingInterface< Dimensionality, TPixel >
itkImageMovingInterface< Dimensionality, TPixel >,
itkImageFixedMaskInterface< Dimensionality, unsigned char >,
itkImageMovingMaskInterface< Dimensionality, unsigned char >
>,
Providing<
elastixTransformParameterObjectInterface< itk::Image< TPixel, Dimensionality >, itk::Image< TPixel, Dimensionality >>,
......@@ -94,6 +98,10 @@ public:
virtual int Accept( typename itkImageMovingInterface< Dimensionality, TPixel >::Pointer ) override;
virtual int Accept( typename itkImageFixedMaskInterface< Dimensionality, unsigned char >::Pointer ) override;
virtual int Accept( typename itkImageMovingMaskInterface< Dimensionality, unsigned char >::Pointer ) override;
// Providing Interfaces:
virtual elastixTransformParameterObject * GetTransformParameterObject() override;
......@@ -101,8 +109,11 @@ public:
virtual void Update() override;
//Base class methods:
virtual bool MeetsCriterion( const CriterionType & criterion ) override;
virtual bool ConnectionsSatisfied() override;
static const char * GetDescription() { return "MonolithicElastix Component"; }
private:
......
......@@ -67,6 +67,26 @@ MonolithicElastixComponent< Dimensionality, TPixel >::Accept( typename itkImageM
return 0;
}
template< int Dimensionality, class TPixel >
int
MonolithicElastixComponent< Dimensionality, TPixel >::Accept(typename itkImageFixedMaskInterface< Dimensionality, unsigned char >::Pointer component)
{
auto fixedMaskImage = component->GetItkImageFixedMask();
// connect the itk pipeline
this->m_elastixFilter->SetFixedMask(fixedMaskImage);
return 0;
}
template< int Dimensionality, class TPixel >
int
MonolithicElastixComponent< Dimensionality, TPixel >::Accept(typename itkImageMovingMaskInterface< Dimensionality, unsigned char >::Pointer component)
{
auto movingMaskImage = component->GetItkImageMovingMask();
// connect the itk pipeline
this->m_elastixFilter->SetMovingMask(movingMaskImage);
return 0;
}
template< int Dimensionality, class TPixel >
typename MonolithicElastixComponent< Dimensionality, TPixel >::ItkImagePointer
......@@ -146,4 +166,25 @@ MonolithicElastixComponent< Dimensionality, TPixel >
}
return meetsCriteria;
}
template< int Dimensionality, class TPixel >
bool
MonolithicElastixComponent< Dimensionality, TPixel >
::ConnectionsSatisfied()
{
// This function overrides the default behavior, in which all accepting interfaces must be set.
// Only Fixed and Moving images are required
// TODO: see I we can reduce the amount of code with helper (meta-)functions
if (!this->InterfaceAcceptor< itkImageFixedInterface< Dimensionality, TPixel >>::GetAccepted())
{
return false;
}
if (!this->InterfaceAcceptor< itkImageMovingInterface< Dimensionality, TPixel >>::GetAccepted())
{
return false;
}
return true;
}
} //end namespace selx
......@@ -20,7 +20,6 @@
#include "selxSuperElastixFilterCustomComponents.h"
#include "elxParameterObject.h"
#include "selxElastixComponent.h"
#include "selxMonolithicElastixComponent.h"
#include "selxMonolithicTransformixComponent.h"
#include "selxItkImageSinkComponent.h"
......@@ -46,18 +45,22 @@ public:
typedef DataManager DataManagerType;
/** Make a list of components to be registered for this test*/
typedef TypeList< ElastixComponent< 2, float >,
typedef TypeList<
MonolithicElastixComponent< 2, float >,
MonolithicTransformixComponent< 2, float >,
ItkImageSinkComponent< 2, float >,
DisplacementFieldItkImageFilterSinkComponent< 2, float >,
ItkImageSourceComponent< 2, float >,
ItkImageSourceComponent< 2, unsigned char >, //for masks
ItkImageSourceComponent< 3, double >> RegisterComponents;
typedef itk::Image< float, 2 > Image2DType;
typedef itk::ImageFileReader< Image2DType > ImageReader2DType;
typedef itk::ImageFileWriter< Image2DType > ImageWriter2DType;
typedef itk::Image< unsigned char, 2 > Mask2DType;
typedef itk::ImageFileReader< Mask2DType > MaskReader2DType;
typedef itk::Image< itk::Vector< float, 2 >, 2 > DisplacementImage2DType;
typedef itk::ImageFileWriter< DisplacementImage2DType > DisplacementImageWriter2DType;
......@@ -86,69 +89,6 @@ public:
DataManagerType::Pointer dataManager;
};
TEST_F( ElastixComponentTest, ImagesOnly )
{
/** make example blueprint configuration */
BlueprintPointer blueprint = Blueprint::New();
ParameterMapType component0Parameters;
component0Parameters[ "NameOfClass" ] = { "ElastixComponent" };
component0Parameters[ "RegistrationSettings" ] = { "rigid" };
component0Parameters[ "MaximumNumberOfIterations" ] = { "2" };
component0Parameters[ "Dimensionality" ] = { "2" };
component0Parameters[ "PixelType" ] = { "float" };
component0Parameters[ "ResultImagePixelType" ] = { "float" };
blueprint->SetComponent( "RegistrationMethod", component0Parameters );
ParameterMapType component1Parameters;
component1Parameters[ "NameOfClass" ] = { "ItkImageSourceComponent" };
component1Parameters[ "Dimensionality" ] = { "2" }; // should be derived from the inputs
blueprint->SetComponent( "FixedImageSource", component1Parameters );
ParameterMapType component2Parameters;
component2Parameters[ "NameOfClass" ] = { "ItkImageSourceComponent" };
component2Parameters[ "Dimensionality" ] = { "2" }; // should be derived from the inputs
blueprint->SetComponent( "MovingImageSource", component2Parameters );
ParameterMapType component3Parameters;
component3Parameters[ "NameOfClass" ] = { "ItkImageSinkComponent" };
component3Parameters[ "Dimensionality" ] = { "2" }; // should be derived from the inputs
blueprint->SetComponent( "ResultImageSink", component3Parameters );
ParameterMapType connection1Parameters;
//connection1Parameters["NameOfInterface"] = { "itkImageFixedInterface" };
blueprint->SetConnection( "FixedImageSource", "RegistrationMethod", connection1Parameters );
ParameterMapType connection2Parameters;
//connection2Parameters["NameOfInterface"] = { "itkImageMovingInterface" };
blueprint->SetConnection( "MovingImageSource", "RegistrationMethod", connection2Parameters );
ParameterMapType connection3Parameters;
//connection3Parameters["NameOfInterface"] = { "GetItkImageInterface" };
blueprint->SetConnection( "RegistrationMethod", "ResultImageSink", connection3Parameters );
// Set up the readers and writers
ImageReader2DType::Pointer fixedImageReader = ImageReader2DType::New();
fixedImageReader->SetFileName( dataManager->GetInputFile( "BrainProtonDensitySliceBorder20.png" ) );
ImageReader2DType::Pointer movingImageReader = ImageReader2DType::New();
movingImageReader->SetFileName( dataManager->GetInputFile( "BrainProtonDensitySliceR10X13Y17.png" ) );
ImageWriter2DType::Pointer resultImageWriter = ImageWriter2DType::New();
resultImageWriter->SetFileName( dataManager->GetOutputFile( "ElastixComponentTest_BrainProtonDensity.mhd" ) );
superElastixFilter->SetInput( "FixedImageSource", fixedImageReader->GetOutput() );
superElastixFilter->SetInput( "MovingImageSource", movingImageReader->GetOutput() );
resultImageWriter->SetInput( superElastixFilter->GetOutput< Image2DType >( "ResultImageSink" ) );
EXPECT_NO_THROW( superElastixFilter->SetBlueprint( blueprint ) );
// Update call on the writers triggers SuperElastix to configure and execute
resultImageWriter->Update();
}
TEST_F( ElastixComponentTest, MonolithicElastixTransformix )
{
/** make example blueprint configuration */
......@@ -165,6 +105,10 @@ TEST_F( ElastixComponentTest, MonolithicElastixTransformix )
blueprint->SetComponent( "MovingImageSource", { { "NameOfClass", { "ItkImageSourceComponent" } }, { "Dimensionality", { "2" } } } );
blueprint->SetComponent( "FixedMaskImageSource", { { "NameOfClass", { "ItkImageSourceComponent" } }, { "Dimensionality", { "2" } } } );
blueprint->SetComponent( "MovingMaskImageSource", { { "NameOfClass", { "ItkImageSourceComponent" } }, { "Dimensionality", { "2" } } } );
blueprint->SetComponent( "ResultImageSink", { { "NameOfClass", { "ItkImageSinkComponent" } }, {