This is not the easiest project, but as long as you're experimenting and getting your hands dirty with ITK, you're doing alright!
As hinted in the project description, you're probably dealing with purely rotation + translation for the post-landmark ITK-based transform, so here's a new friend:
transform = itk.Euler3DTransform[itk.D].New() transform.SetIdentity() registration_method.SetInitialTransformParameters(transform.GetParameters() )
With this transform, a RegularStepGradientDescentOptimizer() does the trick just fine.
Also, association of the registration components with your registration method should look something like this:
reg_method.SetOptimizer(optimizer.GetPointer()) reg_method.SetTransform(transform.GetPointer()) reg_method.SetMetric(metric.GetPointer()) reg_method.SetInterpolator(interpolator.GetPointer())
(you need the .GetPointer() to make it work. This has to do with ITK API: most objects are smartpointers, but some setters require nomal pointers)
Important tips
- This exercise is all about volume-to-volume registration, and NOT polydata-to-polydata.
- Downsample both the datasets, this accelerates your work and minimises crashes. Write your downsampled sets away with the metaImage writer, then you can load them directly with the ITKReader, no VTKtoITK conversion required.
- There are plenty of good ITK registration examples. Think of the ITK Handbook for example...
- Pay careful attention to the type and the dimension of your ITK filters. It's sometimes just easier, if your data has been downsampled, to stick to 3D floating point.
Use the bottom-window of the CodeRunner to your advantage. Even after you've executed something that generated an error, you should have access to all created instances, and you can query them to find out how they work.
Learn to use the online ITK class descriptions and the book.
See this as a learning experience. It's a hard skill that you're picking up, but it's a very marketable skill.
More tips
some optimisers (like the one above), minimise by default. Others maximise. Certain similarity metrics want to be minimised, others want to be maximised. For example, MeanSquared == mean squared differences -> wants to be minimsed so there's no problem with a gradient *descent*. Correlation metrics want to be maximised on the other hand, so you should use the optimiser's SetMaximum(1) method.
- To see what your optimiser is doing, try something like the following in the setup tab, shortly after you've instantiated the optimiser:
def observer_iteration(): print optimizer.GetCurrentIteration(), "optimizer value", optimizer.GetValue(), "steplength", optimizer.GetCurrentStepLength() p2c = itk.PyCommand.New() p2c.SetCommandCallable(observer_iteration) optimizer.AddObserver(itk.IterationEvent(), p2c.GetPointer())
The "GetCurrentStepLength" is obviously not relevant for all optimisers. In the case of the RegularStepGradientDescent it is. The step should get smaller and smaller. A too large initial step length will also get you into trouble, as the optimiser can jump right out of your image. SetMaximumStepLength() can help with this.
And finally
If you:
- have a good pre-registration with the landmark (visualised) and
- your rigid registration has gone in more or less the right direction (i.e .improved slightly over the pre-reg) and
- you can show a volume rendering of the differences between the "final" two volumes (also in comparison to a rendering of the pre-reg results, showing the improvement, small as it may be; sub-volumes are your friend)
then:
You have gone through the whole pipeline of data loading, visualisation, exploration, interactive pre-registration, ITK-based registration and visualisation of results, which means that you now have the mental tools to approach this kind of research problem in practice.
so:
You're done and you can send me your one page report.
