Converting a 2D numpy array to a VTK image

Hello,

I am trying to detect edges in a volume and therefore, I use OpenCV. After converting my slice to a numpy array and detecting the edges, I am able to display it in my Jupyter notebook. Here is the result I get:
image

However, when I try to display this in 3D slicer, the image gets displayed on 2 different slices and the orientation isn’t right. Here’s what I mean:
image
image
I’m not really sure where I went wrong as I have tried to change the dimensions in multiple different ways but nothing seems to be working. Is there something that I missed to be able to fix this issue?

Here is my code:

data_type = vtk.VTK_UNSIGNED_CHAR
imageSpacing = [0.1, 0.1, 0.1]
imageDirections = [[1,0,0], [0,1,0], [0,0,1]]
shape = edges.shape
print("tableau contours:", shape)
dims = volume.GetImageData().GetDimensions()
print("tableau dimensions volume:", dims)

### edges = edges[:,np.newaxis,:]
flat_data_array = edges.flatten()
vtk_data = numpy_support.numpy_to_vtk(num_array=flat_data_array, deep=True, array_type=data_type)

plt.figure()
plt.title("Résultat détection de contours")
plt.imshow(edges, cmap='gray')
plt.show()

result = vtk.vtkImageData()
result.GetPointData().SetScalars(vtk_data)
result.SetDimensions(60, 1, 512)

result.Modified()
result_volume = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode')
result_volume.SetSpacing(imageSpacing)
result_volume.SetIJKToRASDirections(imageDirections)
result_volume.SetAndObserveImageData(result)
result_volume.CreateDefaultDisplayNodes()

slicer.util.updateVolumeFromArray(result_volume, edges)

### result_volume.SetIJKToRASDirections(imageDirections)


slicer.util.updateVolumeFromArray(result_volume, edges)

Thank you in advance.

OpenCV is for computer vision. It is very good for image processing for computer vision but not well suited for medical image processing. For example, most OpenCV functions operate in pixel space (instead of physical space), cannot deal with arbitrarily oriented images (ignores IJKtoRAS), many functions don’t work on 3D images, and in general its developers did not think about medical image processing needs when they implemented the library.

Most likely you will have much simpler life and get better results if you use ITK instead. You can either use SimpleITK or ITK-Python. There is a simple example of using SimpleITK in Slicer here.

My issue with SimpleITK is tha whenever I try to execute the CannyEdgeDetectionImageFilter(), I get an error that I don’t quite understand and I don’t know where it’s coming from. Here is my error:

RuntimeError: Exception thrown in SimpleITK CannyEdgeDetectionImageFilter_Execute: sitk::ERROR: Pixel type: 16-bit unsigned integer is not supported in 3D by class slicer_itk::simple::CannyEdgeDetectionImageFilter.

It seems that the scalar type of your image is not supported byu the Canny filter. You can cast it to 8 bits if it does not mean too much data loss for you.

The other option is to make sure the geometry of the image is preserved after the OpenCV processing. Here is some background about this
https://slicer.readthedocs.io/en/latest/user_guide/coordinate_systems.html
But basically what you’ll need is to set the numpy output to a volume that has the same geometry as the input. What I’d do is I’d clone the input volume using subject hierarchy and then use that cloned node with the updateVolumeFromArray method.
https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#clone-a-node

Thank you very much, that was very helpful !

1 Like