How to debug python library written in C++

I have a Scripted module which invokes functionality from ITKUltrasound. However, the result of this invocation is an all-zero image. Python unit test in ITKUltrasound itself produces non-zero output. What is the best way to debug this situation? ITK is pip-installed from PyPI.

You could try writing all the inputs into files and run it using the unit test. That should tell if the problem is in how your module prepares the input data or reads the output data; or the problem is in the Python package. If you have the answer for this then I can further ideas for next steps.

Slicer unit test is equivalent to ITKUltrasound unit test with regard to input image and parameters. And the image is non-zero just before passing it into the function.

I’d try breaking this down to the simplest operations that you know should be working, like passing an image back and forth unchanged, then adding a small filter, and ultimately getting back to the full pipeline. I don’t think using the pypi ITK has gotten a lot of testing in Slicer.

I am the first kitten for testing ITK from PyPI :smile:

I have another module which works, so at least one filter works correctly.

Running this from test script from within Slicer produces expected result:

import math
import itk

itk.auto_progress(2)

pixel_type = itk.UC
dimension = 3
image_type = itk.CurvilinearArraySpecialCoordinatesImage[pixel_type, dimension]
reader = itk.ImageFileReader[image_type].New()
reader.SetFileName(r"C:\Dev\SlicerITKUltrasound\ScanConvertCurvilinearArray\Data\ScanConvertCurvilinearArrayTestInput.mha")
reader.Update()
image = reader.GetOutput()
image.DisconnectPipeline()

image.SetLateralAngularSeparation(0.00862832)
image.SetFirstSampleDistance(26.4)
image.SetRadiusSampleSize(0.0513434)

output_size = [800, 800, 1]
output_spacing = [0.15] * dimension
output_origin = [output_size[0] * output_spacing[0] / -2.0, 26.4 * math.cos(math.pi / 4.0), 0.0]

result = itk.resample_image_filter(image, size=output_size, output_spacing=output_spacing, output_origin=output_origin)
itk.imwrite(result, r"C:\Dev\SlicerITKUltrasound\ScanConvertCurvilinearArray\Data\Output.mha")

This confirms that ITK within Slicer works. This at least narrows the problem down to how parameters are passed.

After more debugging, I discovered that I forgot to call inputImage.SetRegions(itkImage.GetLargestPossibleRegion()), which should go just before inputImage.SetPixelContainer(itkImage.GetPixelContainer()).

1 Like

@dzenanz Could you implement utility functions for passing images between ITK and Slicer that we could put into slicer.util? If there is a way to do it without writing to file (e.g., via numpy arrays) then it is nice. But the utility functions would help even if we used files, as it would make things easier for users, we could ensure that optimal settings are used (we could automatically generate a filename, use a problem-free file format, turn off compression, etc.), and later we could further optimize things with in-memory transfer.

We have an example in the Slicer script repository for using SimpleITK in Slicer. If we have the utility functions then we could add one or few examples for ITKPython filters, too. You can edit this page directly and send a pull request with the suggested changes.

I think that getITKImageFromVolumeNode and setITKImageToVolumeNode are ready for migration to slicer.util. Perhaps along with a rename, and a bit of parameter documentation. You might see some opportunity for refactoring, too. They already use in-memory transfer. I don’t remember any more whether the pixel array is copied or just referenced. They can be taken from here:

The usage examples can be inspired by:

Could we get this into slicer.util before cutting the next release? When is the timeline for that? I am on vacation 17th-28th. I added a few more modules which use these functions, see pythonization branch.

I just created a PR to do this:

1 Like