# How to debug python library written in C++

**URL:** <https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364>\
**Category:** Development\
**Tags:** python, itk, itk-python\
**Created:** [May 8, 2023, 9:13pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364 "2023-05-08T21:13:14Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [May 8, 2023, 9:13pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/1 "2023-05-08T21:13:14Z")

</div>

I have a [Scripted module](https://github.com/KitwareMedical/SlicerITKUltrasound/blob/pythonizationDev/ScanConvertCurvilinearArray/ScanConvertCurvilinearArray.py) which invokes functionality from ITKUltrasound. However, the result of this invocation is an all-zero image. Python [unit test](https://github.com/KitwareMedical/ITKUltrasound/commit/86aff3587a7efbb028e2ca210e2ad46424a1b964) in ITKUltrasound itself produces non-zero output. What is the best way to debug this situation? ITK is [pip-installed from PyPI](https://github.com/KitwareMedical/SlicerITKUltrasound/blob/d230be0c980cc1e14e047f0d5376b714354fcf59/ITKUltrasoundCommon/ITKUltrasoundCommon.py#L92).

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 9, 2023, 1:06am UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/2 "2023-05-09T01:06:32Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [May 9, 2023, 12:54pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/3 "2023-05-09T12:54:34Z")

</div>

Slicer [unit test](https://github.com/KitwareMedical/SlicerITKUltrasound/blob/d230be0c980cc1e14e047f0d5376b714354fcf59/ScanConvertCurvilinearArray/ScanConvertCurvilinearArray.py#L485-L486) is equivalent to ITKUltrasound [unit test](https://github.com/KitwareMedical/ITKUltrasound/blob/86aff3587a7efbb028e2ca210e2ad46424a1b964/wrapping/test/CMakeLists.txt#L47-L59) with regard to input image and parameters. And the image is [non-zero](https://github.com/KitwareMedical/SlicerITKUltrasound/blob/d230be0c980cc1e14e047f0d5376b714354fcf59/ScanConvertCurvilinearArray/ScanConvertCurvilinearArray.py#L342) just before passing it into the function.

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [May 9, 2023, 1:30pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/4 "2023-05-09T13:30:54Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [May 9, 2023, 1:49pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/5 "2023-05-09T13:49:46Z")

</div>

I am the first kitten for testing ITK from PyPI 😄

I have another [module which works](https://github.com/KitwareMedical/SlicerITKUltrasound/blob/d230be0c980cc1e14e047f0d5376b714354fcf59/BModeFromRF/BModeFromRF.py#L281-L290), so at least one filter works correctly.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [May 9, 2023, 5:09pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/6 "2023-05-09T17:09:46Z")

</div>

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

```auto
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.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [May 9, 2023, 5:52pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/7 "2023-05-09T17:52:05Z")

</div>

After more debugging, I discovered that I forgot to call `inputImage.SetRegions(itkImage.GetLargestPossibleRegion())`, which should go just before [`inputImage.SetPixelContainer(itkImage.GetPixelContainer())`](https://github.com/KitwareMedical/SlicerITKUltrasound/blob/d230be0c980cc1e14e047f0d5376b714354fcf59/ScanConvertCurvilinearArray/ScanConvertCurvilinearArray.py#L387).

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 10, 2023, 3:19am UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/8 "2023-05-10T03:19:30Z")

</div>

@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](https://github.com/Slicer/Slicer/blob/main/Docs/developer_guide/script_repository/volumes.md#running-an-itk-filter-in-python-using-simpleitk) directly and send a pull request with the suggested changes.

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [May 10, 2023, 2:19pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/9 "2023-05-10T14:19:14Z")

</div>

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:

> <https://github.com/KitwareMedical/SlicerITKUltrasound/blob/29a07dfc7714431182151b784356a0ab15afe59f/ITKUltrasoundCommon/ITKUltrasoundCommon.py#L98-L204>

The usage examples can be inspired by:

> <https://github.com/KitwareMedical/SlicerITKUltrasound/blob/29a07dfc7714431182151b784356a0ab15afe59f/BModeFromRF/BModeFromRF.py#L279-L290>

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [July 12, 2023, 5:14pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/10 "2023-07-12T17:14:24Z")

</div>

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](https://github.com/KitwareMedical/SlicerITKUltrasound/tree/pythonization).

---

<div class="post-metadata">

**Author:** ![dzenanz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/dzenanz/32/1992_2.png) [@dzenanz](https://discourse.slicer.org/u/dzenanz)\
**Post date:** [July 14, 2023, 6:28pm UTC](https://discourse.slicer.org/t/how-to-debug-python-library-written-in-c/29364/11 "2023-07-14T18:28:32Z")

</div>

I just created a PR to do this:

> <https://github.com/Slicer/Slicer/pull/7094>
>
> This was adapted from SlicerITKUltrasound extension at 6d7ea98bc329fc52e9c9bfddc…cc807cf76806fc2. Direct link: https://github.com/KitwareMedical/SlicerITKUltrasound/blob/6d7ea98bc329fc52e9c9bfddccc807cf76806fc2/ITKUltrasoundCommon/ITKUltrasoundCommon.py#L98-L204
