ITK Workshop Software Design October 5-8, 2005
ITK Workshop – Open Source Viewers Design Patterns Pipeline Decorators Iterators Adaptors Accessors Functors Factories Traits
Insight Toolkit - Advanced Course The Data Pipeline
The Data Pipeline Data Object Process Object Data Object Process
Who owns the Output ? Data Object Data Object Data Object Process Object Process Object The ProcessObject allocates and owns its output Therefore the output is const
Instantiate the filters types Construct the filters with New() How to use the Pipeline ? Instantiate the filters types Construct the filters with New() Connect SetInput() GetOutput() Set filter Parameters Invoke Update() in the last filter
The Pipeline is intended to be Re-Executed Modify the filter parameters Invoke Update() in the last filter Only the filters that need to re-compute their output will be re-executed
Updating the Pipeline Image Reader Filter A Filter B Filter C Filter D Image Writer
Updating the Pipeline Image Reader Filter A Filter B Filter C Execute Update() Filter D Image Writer Execute Execute
Updating the Pipeline Update() Image Reader Filter A Filter B Filter C Execute Execute Execute Execute Filter D Image Writer
Updating the Pipeline Update() SetParameter Image Reader Filter A Filter B Filter C I’m OK I’m OK Execute Execute Filter D Image Writer
How it works internally itk::Object has a TimeStamp Invoking Modified() updates the stamp SetParameter() methods calls Modified() Most Set() methods use itkSetMacro() If you write your own SetParameter() you must remember to invoke Modified()
How it works internally Quiz ! What will go wrong with your filter if you create a SetParameter() method and forget to invoke Modified() from it ? It will run fine the first time but if you call SetParameter() and then call Update() the filter will not re-execute.
How it works internally Exercise Open the itkMacro.h file in Insight/Code/Common Find the itkSetMacro() and identify the line where Modified() is invoked
Insight Toolkit - Advanced Course Pervasive Pipelining (or the Ode to Intelligent Design)
Insight Toolkit - Advanced Course In the Beginning… there was the itk::DataObject The itk::DataObject originated the itk::Image and the itk::Mesh
Insight Toolkit - Advanced Course Only the the itk::DataObject and his sons were admited in the Pipeline garden The float, ints, Transforms and Points grew jealous of the DataObject Since they could not promenade in the Pipeline Garden
Insight Toolkit - Advanced Course So in chorus they asked to change the ways of the Pipeline garden And from the darkness of the abyss the itk::DataObjectDecorator was born.
Insight Toolkit - Advanced Course With it, the float, ints, Transforms and Points became as DataObjects and walked at their will in the Pipeline Garden.
The SimpleDataObjectDecorator #include “itkDataObject.h” template<class T> class SimpleDataObjectDecorator : public DataObject { public: typedef SimpleDataObjectDecorator Self; typedef DataObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; itkNewMacro( Self ); itkTypeMacro( SimpleDataObjectDecorator, DataObject ); private: T m_Component;
The SimpleDataObjectDecorator public: virtual void Set( const T & value ) { if( m_Component != value ) { m_Component = value; this->Modified(); } } virtual const T & Get() const { return m_Component }
The DataObjectDecorator #include “itkDataObject.h” template<class T> class DataObjectDecorator : public DataObject { public: typedef DataObjectDecorator Self; typedef DataObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; itkNewMacro( Self ); itkTypeMacro(DataObjectDecorator, DataObject ); private: typename T::Pointer m_Component;
The DataObjectDecorator public: virtual void Set( const T * value ) { if( m_Component != value ) { m_Component = value; this->Modified(); } } virtual const T * Get() const { return m_Component }
Insight Toolkit - Advanced Course Iterators
STL Iterators Image Iterators Mesh Iterators Region Linear Slice VectorContainer MapContainer
STL Iterators #include <vector> typedef std::vector< float > VectorType; VectorType myVector; myVector.push_back( 4 ); myVector.push_back( 7 ); myVector.push_back( 19 ); VectorType::const_iterator itr = myVector.begin(); while( itr != myVector.end() ) { std::cout << *itr << std::endl; ++itr; }
The Evil nested loop and the Dimensional Trap Image Iterators The Evil nested loop and the Dimensional Trap for( unsigned int z = 0; z < Nz; z++ ) { for( unsigned int y = 0; y < Ny; y++ ) { for( unsigned int x = 0; x < Nx; x++ ) { image[z][y][z] = … // pixel access } How to generalize this code to 2D, 3D, 4D …?
Image Iterators (const) #include “itkImage.h” #include “itkImageRegionIterator.h” typedef itk::Image< float, 3 > ImageType; typedef itk::ImageRegionConstIterator< ImageType > IteratorType; ImageType::ConstPointer image = GetConstImageSomeHow(); ImageType::RegionType region = image->GetLargestPossibleRegion(); IteratorType itr( image, region ); itr.GoToBegin(); while( ! itr.IsAtEnd() ) { std::cout << itr.Get() << std::endl; ++itr; }
Image Iterators (non-const) #include “itkImage.h” #include “itkImageRegionIterator.h” typedef itk::Image< float, 3 > ImageType; typedef itk::ImageRegionIterator< ImageType > IteratorType; ImageType::Pointer image = GetNonConstImageSomeHow(); ImageType::RegionType region = image->GetLargestPossibleRegion(); IteratorType itr( image, region ); itr.GoToBegin(); while( ! itr.IsAtEnd() ) { itr.Set( 0 ); ++itr; }
Image Linear Iterator (const) #include “itkImage.h” #include “itkImageLinearIteratorWithIndex.h” typedef itk::Image< float, 3 > ImageType; typedef itk::ImageLinearConstIteratorWithIndex< ImageType > IteratorType; ImageType::ConstPointer image = GetConstImageSomeHow(); ImageType::RegionType region = GetImageRegionSomeHow(); IteratorType itr( image, region ); for( itr.GoToBegin(); !itr.IsAtEnd(); itr.NextLine() ) { while( ! itr.IsAtEndOfLine() ) { std::cout << itr.Get() << “:“ << itr.GetIndex() << std::endl; ++itr; }
Image Slice Iterator (const) #include “itkImageSliceIteratorWithIndex.h” typedef itk::ImageSliceConstIteratorWithIndex<ImageType> IteratorType; ImageType::ConstPointer image = GetConstImageSomeHow(); ImageType::RegionType region = GetImageRegionSomeHow(); IteratorType itr( image, region ); for( itr.GoToBegin(); !itr.IsAtEnd(); itr.NextSlice() ) { for( ; ! itr.IsAtEndOfSlice(); itr.NextLine() ) { for( ; ! itr.IsAtEndOfLine(); ++itr ) { std::cout << itr.Get() << “:“ << itr.GetIndex() << std::endl; }
Using the ImageLinearIterator Exercise 28 Use two ImageLinearIterators in order to flip and image across its diagonal
Reflective Image Iterator Neighborhood Iterator Other Image Iterators Reflective Image Iterator Neighborhood Iterator Shaped Neighborhood Iterator Image Random Iterator Image Random Non Repeating Iterator Flood Filled Function Conditional Iterator Path Iterator
Importance of Image Iterators Iterators absorb a large portion of the complexity in an Image Filter Iterators made possible to generalize ITK to N-Dimensional images Iterators allows to have fast access to pixel data Iterators made ImageAdaptors possible (see later)
Mesh Iterators (const) #include “itkMesh.h” typedef itk::Mesh< float, 3 > MeshType; MeshType::ConstPointer mesh = GetConstMeshSomeHow(); typedef MeshType::PointsContainer PointsContainer; typedef PointsContainer::ConstIterator PointsIterator; PointsContainer points = mesh->GetPoints(); PointsIterator pointItr = points->Begin(); while( pointItr != points->End() ) { MeshType::PointsType point = pointItr.Value(); std::cout << “point “ << point << std::endl; ++pointItr; }
Insight Toolkit - Advanced Course Image Adaptors (Things are not always what they seem)
Some operations are too simple for a filter Image Filter Image Image Process Object Image Adaptor Image Fake Image Image Adaptor
Image Adaptors = Image Iterators + Pixel Accessors Fake Image Image Adaptor Pixel Accessor Pixel Accessor Where the transformation is done
Pixel Accessor and Image Iterators
Pixel Accessor and Image Iterators m_PixelAccessorFunctor.Get( *( m_Buffer + m_Offset )); itr.Get()
Pixel Accessor : Red Channel from RGB Image namespace Accessor { class RedChannel { public: typedef itk::RGBPixel<float> InternalType; typedef float ExternalType; static ExternalType Get()( const InternalType & A ) { return static_cast< ExternalType >( A.GetRed() ); } }; } // end of Accessor namespace
Using the Pixel Accessor in the Image Adaptor int main() { typedef Accessor::RedChannel::InternalType InternalPixelType; typedef itk::Image< InternalPixelType, 2 > AdaptedImageType; typedef itk::ImageAdaptor< AdaptedImageType, Accessor::RedChannel >ImageAdaptorType; ImageAdaptorType::Pointer adaptor = ImageAdaptorType::New(); typedef itk::ImageFilterReader< AdaptedImageType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); adaptor->SetImage( reader->GetOutput() );
Image Adaptors are like Images Image Adaptors are not Filters Image Adaptors do not have Buffers Not all Iterators support Image Adaptors Not all Filters support Image Adaptors
Using the Pixel Accessor in the Image Adaptor typedef itk::Image< unsigned char, 2 > OutputImageType; typedef itk::RescaleIntensityImageFilter< ImageAdaptorType, OutputImageType > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( adaptor ); // Adaptors are like Images !! writer->SetInput( filter->GetOutput() ); writer->Update(); }
Pierce the Illusion… to see the Void. Image Adaptors Pierce the Illusion… to see the Void.
Image Adaptors Writers access Image’s buffer directly, They are not fooled by Image Adaptors. Neigborhood Iterators do not call the Get() method… They are not fooled by Image Adaptors.
Image Adaptors Exercise 27 Write a Pixel Accessor that will invert the intensities in an image. Instantiate its corresponding Image Adaptor
Pixel Accessor : Invert 8-bits intensities. namespace Accessor { class Inversor { public: typedef unsigned char InternalType; typedef unsigned char ExternalType; static ExternalType Get()( const InternalType & A ) { return static_cast< ExternalType >( 255 - A ); } }; } // end of Accessor namespace
Insight Toolkit - Advanced Course Functors The Way takes no action, but leaves nothing undone
Functors A Functor is a class that looks like a Function namespace Functor { template< class TInput, class TOutput> class Doubler { public: Doubler() {}; ~Doubler() {}; inline TOutput operator()( const TInput & A ) { return static_cast<TOutput>( 2.0 * A ); } }; } // end of Functor namespace
Functors Functors are used as functions typedef Functor::Doubler< int, int > FunctorType; FunctorType iDouble; int input = 197.0; int result = iDouble( input );
Insight Toolkit - Advanced Course Functors are used by UnaryFuncturImageFilter<> BinaryFunctorImageFilter<> TernaryFunctorImageFilter<> Functors make possible to factorize all other operations of an image filter
Insight Toolkit - Advanced Course Factories The origin of the world is its mother; Understand the mother, and you understand the child Tao Te Ching
The Class Hierarchy of Factories itk::LightObject itk::Object itk::DataObject itk::ObjectFactoryBase itk::ProcessObject itk::ObjectFactory
What happens when we invoke ::New() ? itk::LightObject itk::ObjectFactory<T> itk::ObjectFactoryBase New() Create() CreateInstance( typeid(T) ) Iterate over Registered Factories CreateObject( classname) return pointer, or NULL if NULL call “new”
Factories itk::ObjectFactoryBase static RegisteredFactories std::list< ObjectFactoryBase *> ObjectFactory X ObjectFactory Y ObjectFactory Z ObjectFactory W static void RegisteredFactory( ObjectFactoryBase *)
Factories can be loaded as Dynamic Libraries itk::ObjectFactoryBase itk::DynamicLoader static LoadLibrariesInPath( char * path ) From path get all filenames that are shared libraries OpenLibrary( fname) GetSymbolAddress() static RegisterFactory( ObjectFactoryBase* )
Loading Dynamic Factories Set environment variable ITK_AUTOLOAD_PATH In the Shared library create an itkLoad() function that returns an itk::ObjectFactoryBase * Compile the shared library using CMake command ADD_LIBRARY( Name SHARED …sources…) Copy the shared library in one of the directories in ITK_AUTOLOAD_PATH
Image Adaptors Exercise 29 Create a simple Factory Use the same structure for replacing an existing ITK class
Creating my own Factory itk::ObjectFactoryBase itk::MyOwnFactory static itkLoad(); FactoryLessNewMacro(); FactoryNew(); GetITKSourceVersion(); GetDescription(); RegisterOneFactory(); MyOwnFactory(); // constructor ~MyOwnFactory(); // destructor
Insight Toolkit - Advanced Course Traits Knowing others is Wisdom Knowing the Self is Enlightenment Tao Te Ching
Traits Types declared inside another Type Fundamental for enforcing consitency
Traits Examples #include “itkImageBase.h” template<class TPixel, unsigned int VDimension> class Image : public ImageBase< VDimension > { public: typedef Image Self; typedef ImageBase< VDimension > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; itkNewMacro( Self ); itkTypeMacro(Image, ImageBase ); typedef TPixel PixelType; itkStaticConstMacro(ImageDimension, unsigned int, VDimension);
Use Traits instead of redundant declarations ! #include “itkImage.h” int main( int, char * argv []) { typedef itk::Image< unsigned char, 2 > ImageType; typedef itk::ImageFilterReader< ImageType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); ImageType::ConstPointer image = reader->GetOutput(); ImageType::IndexType index; index.Fill(0); ImageType::PixelType pixel = reader->GetPixel( index );
Very Important in Return Types… template <class TImage> class ImageFilter { public: typedef TImage ImageType; const ImageType * GetImage() const; private: typename ImageType::ConstPointer m_Image; }; template <class TImage> const typename ImageFilter<TImage>::ImageType * ImageFilter<TImage>::GetImage() const { return m_Image; }
Note the use of typename You need typename if all the following applies You are in a templated class You are accessing a trait from a class that depends on the template arguments of your current class. template <class TImage> class ImageFilter { typename TImage::ConstPointer m_Image; };
Compilers and typename The keyword typename is supported differenty by different compilers Totally ignored by Visual Studio 6.0 Supported in Visual Studio 7.0 and 7.1 Required in gcc 3.2, gcc 3.3 Implicit typenames not supported in gcc 3.4
Insight Toolkit - Advanced Course END