Difference between revisions of "Slicer:Slicer2 Porting to VTK5"
m (Update from Wiki) |
m (1 revision) |
(No difference)
|
Latest revision as of 18:52, 15 May 2008
Home < Slicer:Slicer2 Porting to VTK5Contents
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