VTK
Tips
Generic C++ Tips
Please familiarize yourself with C++ Development. Tips there apply to VTK classes too, which are after all C++ classes.
Minimize includes in headers
The general rule of header files in VTK is that you include only the header file of the superclass. For everything else, you merely forward declare it in the header. See C++ Development#Minimize includes in headers for more info on forward declarations.
This is a good rule to follow where possible in C++ anyways, but in VTK it is particularly important, because many kinds of objects (e.g. STL - Standard Template objects) will cause Python Wrapping of the class to fail if used in the header file.
Use vtkSmartPointers
VTK requires you to create instances of VTK objects only with the class's static New method, like this:
vtkImageData* image = vtkImageData::New();
The reason for this is that VTK uses reference counting internally, and will automatically delete objects that are no longer used. This is very convenient, because you can create an object somewhere, and link it into one or more pipeline, and not have worry about exactly when to delete it. It has however the disadvantage that when objects are created this way, they will not be automatically destroyed when they go out of scope. This is it should be - we only want them destroyed when their reference count goes to zero. However, you must remember to manually call Delete when the object goes out of scope, like this:
image->Delete();
This may not actually delete the object, it merely decrements the reference counter. It's easy to forget this (or to overlook paths that can take you out of scope - for instance if an exception is thrown.) If you forget, this becomes a memory leak. The best solution to this is to use a vtkSmartPointer, like this:
vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New();
This is the only line of code you have to change compared to the abover example - otherwise you can use the variable image exactly as before. Only now, as soon as image goes out of scope, the destructor of the SmartPointer will be called, and it will automatically call the Delete method of image.
NOTE: You don't always need a SmartPointer. If you are getting the pointer to an existing object for some temporary calculation, a bare pointer is adequate.
TIP: You can use regular expressions to search and replace for all creations of VTK objects.
The regular expression search string is:
(vtk\w+) *\* *(\w+) *= *\1::New
And the replace string is:
vtkSmartPointer<$1> $2 = vtkSmartPointer<$1>::New
So for example with sed with would be:
sed -i 's/(vtk\w+) *\* *(\w+) *= *\1::New/vtkSmartPointer<$1> $2 = vtkSmartPointer<$1>::New/' yourfile.cxx
although I actually use TextMate for this.
Note that you amy still have to go through and replace and now-unnecessary delete commands.
Use Doxygen style comments
to write
Example: SimilarityRegistration
SimilarityTransform is a class that I modified heavily to improve the code. Here are some of the changes:
Class Name
Formerly it was called itkSimilarityRegistration . This is confusing, because although it uses ITK classes, it is not itself an ITK class, and does not inherit from an ITK base class, and does not behave like an ITK class. I therefore renamed it to AIMSimilarityRegistration.
Includes moved to .cxx file
See C++_Development#Minimize_includes_in_headers
Comments moved to header file
The comments at the top of the file about what it does and who wrote it and why, get moved from the .cxx file to the .h file (see C++Development#Put_comments_in_the_appropriate_place). (By the way, some of these comments used Doxygen style markers. But Doxygen parses only header files.)
- itkSimilarityRegistration is not, in fact, an itk class. Therefore I renamed it to AIMSimilarityRegistration.
- The static New method and the Delete method are replaced by standard a constructor and destructor. The static New method pattern is only employed for classes created by a Factory object. (The Factory pattern is used in vtk and itk, but this class is neither and has no factory invoker.)
- Added Doxygen style comments to all function calls (at least all the public ones). This makes it much easier for someone to use or modify the class.
- the helper classes CommandIterationUpdate and RegistrationInterfaceCommand seem only intended to be used within AIMSimilarityRegistration, so they get moved to the .cxx file from the .h file.
string handling: previously used char arrays, generally very obtuse code with lots of memory leaks and arbitrary string size limits. Replaced with std::string, std::ostringstream and boost::format. (see [])
Error and warning messages should be to std:cerr, not to std::cout. In most cases this will make no difference, but it gives the user a chance to direct the error messages somewhere different than the regular output. (I often do this for long-running jobs - I want to examine the whole output after it's done, but I want to see errors and warnings immediately.)
vtkFileParser is gone, since it's a reinvention of a standard wheel. Replaced with boost::property_tree. The user can either pass in a set of configuration parameters (SetConfiguration), or specify a file name from which to read the parameters (ReadConfigFile). There is a method VerifyCOnfiguration that will check that all required parameters are present and possible provide default values.
PrintSampleConfigFile now takes a stream as an argument, so it can be used to send the example to Config file to cout, or to a file, or anywhere.
Error handling: done with C++ exceptions. Note that in comparison VTK typically uses return values to indicate error status. Either approach can work, but C++ exceptions more often do the expected thing with less work. (e.g If the caller does not explicitly handle a return value indicating an error, it continues executing as normal, which is bad, whereas if the caller does not explicitly handle an exception, the program terminates, which is good.)
New VTK objects are assigned to a VTKSmartPointer. Previously no Delete was ever called on them, so they were (potentially large) memory leaks. Note that ITK pointers that you get from itk class traits are automatically Smart pointers.
For C+ file streams, this is completely pointless:
if (fout.is_open())
fout.close();
return -1;
Because leaving the scope where fout is defined will call its destructor and close it automatically.
TransformImage had two versions, one for char and one for short. Replaced with a single templated version. Now called like this for example:
TransformImage<short>(outTransform,movingImage,fixedReader,m_movingLog);