Difference between revisions of "Slicer:Slicer2 Porting to VTK5"

From Slicer Wiki
Jump to: navigation, search
m (Update from Wiki)
 
m (1 revision)
 
(No difference)

Latest revision as of 18:52, 15 May 2008

Home < Slicer:Slicer2 Porting to VTK5

Goals

  • To update the Slicer 2 cvs repository, main branch, so that it compiles against the VTK cvs repository, main branch.
  • To preserve backward compatibility so that Slicer 2 will still compile against VTK Slicer-2-6.
  • To update the Slicer 2 tcl files so that Slicer will work with VTK 5.

Methodology

Slicer has a lot of filter written for VTK 4.4 and most filter are subclasses of vtkImageToImageFilter. Eventhough vtkImageToImageFilter in current VTK5 is fully backward compatible, there is a slight issue in this case. Indeed there is a class that used to manipulate vtkImageToImageFilter filters. Therefore in our cases we are facing the problem of supporting filters from VTK (subclasses of vtkImageAlgorithm), and at the same time manipulating slicer's internal classes which are subclasses of vtkImageToImageFilter.

Thefore the trick is the following: use the common parent class: vtkAlgorithm to manipulate pointers. But since this class has no notion of Input or Output we need to cast the pointer to either a vtkImageAlgorithm of vtkImageToImageFilter every time we need to access the input or output.

Another challenge with all the #ifdef was to support with VTK-Tcl Wrapper. Using an external definition and redefining symbols works:

 #define vtkSlicerImageAlgorithm vtkAlgorithm
 #define vtkSlicerImageAlgorithmCppCommand vtkImageAlgorithmCppCommand
 

Here is a skeleton of the solution:

 #ifndef vtkFloatingPointType
 typedef float vtkFloatingPointType;
 #endif
 
 //
 // use an ifdef on SLICER_VTK5 to flag code that won't
 // compile on vtk4.4 and before
 //
 #if ( (VTK_MAJOR_VERSION >= 5) || ( VTK_MAJOR_VERSION == 4 && VTK_MINOR_VERSION >= 5 ) )
 #define SLICER_VTK5
 #endif
 
 
 #include "vtkSlicerBaseWin32Header.h"
 
 // This is a trick to work around a feature in the current vtkWrapTcl
 // This should be removed once slicer fully switch to VTK 5.x execution style
 #ifdef SLICER_VTK5
 #include "vtkAlgorithm.h"
 #include "vtkImageAlgorithm.h"
 #include "vtkImageData.h"
 #include "vtkImageToImageFilter.h"
 // Helper functions for the VTK4.4 / VTK5 intermixed. We need to provide an API
 // with vtkAlgorithm since this is the only common class in between the old ImageToImage filters
 // and the new vtkImageAlgorithm one.
 // Unfortunately there is no notion of Input or Output in vtkAlgorithm therefore
 // we need to try/cast (guess) the real type of the filter in order to get the output:
 inline vtkImageData *GetImageOutput(vtkAlgorithm *filter)
 {
   // Is this a pure VTK class that derive from vtkImageAlgorithm
   vtkImageAlgorithm *ia = vtkImageAlgorithm::SafeDownCast(filter);
   if( ia )
     {
     return ia->GetOutput();
     }
   // else this is a class from Slicer which still derived from vtkImageToImageFilter old layer
   vtkImageToImageFilter *itoi = vtkImageToImageFilter::SafeDownCast(filter);
   if(itoi)
     {
     return itoi->GetOutput();
     }
   // else
   vtkGenericWarningMacro( "Problem executing GetImageOutput() " );
   return NULL;
 }
 inline void SetImageInput(vtkAlgorithm *filter, vtkImageData *output)
 {
   // Is this a pure VTK class that derive from vtkImageAlgorithm
   vtkImageAlgorithm *ia =  vtkImageAlgorithm::SafeDownCast(filter);
   if( ia )
     {
     ia->SetInput(output);
     return;
     }
   // else this is a class from Slicer which still derived from vtkImageToImageFilter old layer
   vtkImageToImageFilter *itoi =  vtkImageToImageFilter::SafeDownCast(filter);
   if(itoi)
     {
     itoi->SetInput(output);
     return;
     }
   // else
   vtkGenericWarningMacro( "Problem executing SetImageInput() " );
 }
 #define vtkSlicerImageAlgorithm vtkAlgorithm
 #define vtkSlicerImageAlgorithmCppCommand vtkImageAlgorithmCppCommand
 #else
 #include "vtkImageToImageFilter.h"
 #include "vtkImageSpatialFilter.h"
 // Dummy stub. See above
 inline vtkImageData *GetImageOutput(vtkImageToImageFilter *filter)
 {
   return filter->GetOutput();
 }
 inline void SetImageInput(vtkImageToImageFilter *filter, vtkImageData *output)
 {
   filter->SetInput(output);
 }
 #define vtkSlicerImageAlgorithm vtkImageToImageFilter
 #define vtkSlicerImageAlgorithmCommand vtkImageToImageFilterCommand
 #define vtkSlicerImageAlgorithmCppCommand vtkImageToImageFilterCppCommand
 #endif
 

See the full implementation here

Notes

Issue with Wrapping

As Nicole pointed out, there is an issue with the wrapping. Indeed the VTK wrapper do not expand the MACRO, therefore having something like:

#define vtkSlicerImageAlgorithm vtkAlgorithm
 

will generate proper c++ code, but the wrapper will generate code like:

temp0 = (vtkSlicerImageAlgorithm *)(vtkTclGetPointerFromObject(argv[2],(char *) "vtkSlicerImageAlgorithm",interp,error));

where vtkSlicerImageAlgorithm is unknown thus leading to:


% vtkImageThreshold thresh
% Slicer SetFirstFilter 0 thresh
vtk bad argument, type conversion failed for object thresh.
Could not type convert thresh which is of type vtkImageThreshold, to type vtkSlicerImageAlgorithm.
Object named: Slicer, could not find requested method: SetFirstFilter
or the method was called with incorrect arguments.
First Solution

Make everything who uses vtkSlicerImageAlgorithm in declaration be a generated a header which CMake can parse therefore the macro expansion is done at CMake level and is transparent for C++ generator and VTK wrappers.

Drawbacks:

  • Generated headers (h.in file), more difficult to read
Second Solution

Make the interface takes as input a vtkObject, and have in the code a check that the input is of type vtkSlicerImageAlgorithm (either a vtkImageToImageFilter for VTK44 or a vtkAlgorithm for VTK5)

  • Drawbacks:

C++ code like :

GetFirstFilter()->GetOutput() ...

will not compile since output is of type vtkObject and not vtkAlgorithm anymore. Tcl is a dynamic langage so it should not a problem there.

Issue with vtkSimpleImageToImageFilter

On a side note the backward compatibily was only designed for vtkDataSetToDataSetFilter. Eg. a filter deriving from vtkSimpleImageToImageFilter is not supported anymore in VTK5. The solution is then to always subclass from vtkImageToImageFilter, or simply move completely to VTK5 in the Slicer3 framework. Another issue is class deriving from vtkSource typically reader. This is not garantee to work with VTK5 unless completely implement the VTK5 pipeline mechanism.

Issue with vtkImageMask

Same problem for filters deriving from let say vtkImageMask. vtkImageMask is now a subclass of vtkImageAlgorithm, therefore the ExecuteData/ExecuteInformation are simply useless. Therefore the question is how to deal whith this particular case. IMHO (Mathieu) to avoid duplicate work those filters should be referenced somewhere and mark as prioritary when switching to VTK5, since these are the one that simply cannot fold under the backward compatibilty layer (unless of course the subclass was wrong and in which case one can still use vtkImageToImageFilter).

List of such classes

  • vtkTensorMask (vtkImageMask)

Methodology (OLD)

Instead of SetInput, we now call SetInputConnection, and instead of SetOutput we now call SetOutputPort. This is for the filter case. For source object you should still call GetOutput.

In VTK 4.x they used to have data set "1" to data set "2" filter, now it simply refer to data set "2" algorithm (what is the output of the algorithm, rather than what is the input AND output).

For C++ classes in Filtering, there are backward compatibility methods in place, no change should be necessary if we subclass from a top level class (ie vtkImageToImageFilter). Unfortunately this is not true for class in IO. You cannot simply subclass fro any vtkDataReader and expect full backward compatibility. Same problem with vtkSimpleImageFilter (although it should be pretty straightforward to move to a vtkImageToImageFilter).

Actual implementation

In C++ files, use this construction to ensure backwards compatibility for classes not in Filtering:

 #if (VTK_MAJOR_VERSION >= 5)
   vtkPolyDataAlgorithm * varName;
 #else
   vtkPolyDataSource * varName;
 #endif

Some conversions:

 VTK 4.x: NumberOfInputs
 VTK 5.x: this->GetNumberOfInputConnections(0);

For a vtkDataReader subclass:

 VTK 4.x: vtkSource::SetNthOutput(0, vtkPolyData::New())
 VTK 5.x: vtkPolyData *output = vtkPolyData::New(); this->SetOutput(output);
 VTK 4.x: Outputs[0]->ReleaseData();
 VTK 5.x: output->ReleaseData();
 VTK 4.x: Outputs[0]->Delete();
 VTK 5.x: output->Delete();

If the TCL wrapping is complaining about the constructions, you can exclude certain code snippets from being wrapped by surrounding them with:

 //BTX
 //ETX

Conclusion

Because of issue:

and

It makes it virtually impossible to maintain backward compatibility with both VTK 4.4.x and VTK 5.x. Therefore it makes more sense to convert those classes directly to VTK 5, some class can be moved to Slicer3 if and only if they are directly subclasses of vtkImageToImageFilter. The conversion to vtkImageAlgorithm is then (for those classes) not an immediate requirement.

Updates

February 2006

N. Aucoin and H. Liu visited Kitware and consulted with M. Malaterre and B. King, working out how to use the VTK 5 backward compatibility classes.

January 2006

Updates made to slicer2/Base/cxx, slicer2/Modules/vtkITK/cxx, slicer2/Modules/vtkTensorUtils/cxx, and slicer2/Modules/vtkDTMRI/cxx so that these components will compile against VTK 5. N. Aucoin, L. O'Donnell, T. Lechner, M. Malaterre

  • update included files to get missing definitions
  • move to vtkPolyDataAlgorithm from vtkPolyDataSource
  • move to GetPolyDataInput(0) from GetInput