Volume with negative spacing turns in strange behaviour (bug?)

Hi,

When I try to add volume with negative spacing in X direction to Slicer I get strange behavior.

For example here is the python code to generate volume and add it to the Slicer (based on example in script repo):

nodeName = "MyNewVolume"
imageSize = [10, 10, 10]
imageOrigin = [0.0, 0.0, 0.0]
imageSpacing = [-1.0, 1.0, 1.0] # `X` spacing is negative

scalars = vtk.vtkDoubleArray()
scalars.SetName("my_scalars")

for i in range(0, imageSize[0]*imageSize[1]*imageSize[2]):
    v = scalars.InsertNextValue(i)

# Create an image volume
imageData = vtk.vtkImageData()
imageData.SetDimensions(imageSize)
imageData.GetPointData().SetScalars(scalars)
# Create volume node
volumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode", nodeName)
volumeNode.SetOrigin(imageOrigin)
volumeNode.SetSpacing(imageSpacing)
volumeNode.SetAndObserveImageData(imageData)
volumeNode.CreateDefaultDisplayNodes()
volumeNode.CreateDefaultStorageNode()

Then I go to the Data module and add the volume to the renderers.
On each slice view I click the button to visualize the volume in 3D view.
Then I go to the Volumes module and then after zooming in/out I can see something similar on the picture:

Originally the volume has nx = ny = nz = 10 points.
If I set the nx to some positive value then there is nothing bad hapen.

I tested it only in Slicer 4.11.2 (see window title) on Ubuntu 20.04 and also in SlicerCAT.

Can’t say for sure but as I far as I know vtkImageData is supposed to work fine with negative spacings and negative origin (at least I could visualize it, can’t say anything about filtering).

What would a volume with negative spacing mean? My understanding is that the spacing values are the physical lengths between adjacent voxels, which could not sensibly be negative. Image directions, on the other hand, could indicate that increasing voxel index values along a dimension correspond to an increasing and negative spatial coordinate value, but this is separate from specifying the size of the voxel itself.

1 Like

I would not give much phisical sense to spacing. I think the phisical or any other meaning should have the grid that is created (in this case the gfid is given by the image). Spacing is just a mean to create the grid and nothong more.

My special use case is the following:
I have few gigabytes (or eve more) of scalar data that should be displayed as vtkImageData. But the data is loaded to the RAM and if I use only positive spacing then I would need either to rearrange the data (very bad solution) or apply transforms or mirror filter. Ok I can do that but I always keep in mind why devellopers dont want add negave spacings :slight_smile:

I dont know is it such a big deal or it is a matter of phisics?

Definitely spacing is a distance and should never be negative. @mikebind is correct that direction vectors can have negative elements because they are relative to a reference frame. In Slicer we always have unit spacing and zero origin in the vtkImageData and encode origin, spacing, and directions in the ijkToRAS matrix (which is another name for index to physical).

@pieper thank you for response,

unit spacing mean the spacing should always be equal to 1 or unit mean that spacing has some units (meter or millimeter or something)? Also does Slicer suppose that the origin should always be set to 0 and should not be set to any other (positive or negative) value?

It just means that we use the vtkImageData as an array to hold only index space values. All the physical space values are in the ijkToRAS matrix. Yes, as a concept spacing should always be positive even if the data type is inherently signed. Generally people don’t have checks for such things for efficiency.

Spacing values are the column norms of the orientation section (top-left 3x3 submatrix) of the IJK to RAS matrix, so these values are always positive. You can write negative values in the orientation submatrix, but ITK and VTK put some limitations on these, and since we use ITK and VTK throughout Slicer, these limitations apply to Slicer, too:

  • the IJK to RAS matrix must be orthogonal
  • IJK axes form a right-handed coordinate system (determinant of IJK to RAS matrix must be positive)
  • the above restrictions are not universally enforced and they are specifically allowed in certain classes, such as in the image class and image IO classes (therefore it is allowed to read an image with arbitrary IJK to RAS matrix and store it in a VTK or ITK image until it is converted to an image that conforms to the requirements).

If you only need to do certain image processing or visualization steps then you can test that they work correctly your non-conformant image. If you want to allow a user to do arbitrary operations on the image then you must resample the voxel array so that you have an orthogonal IJK to RAS matrix with positive determinant.

If the only issue is right-handedness then you can mirror the voxel array along any axis. The easiest is to do it along the third axis, which is simply reorder slices. This is a very fast operation, even for huge volumes.

2 Likes

Thank all for the informative answers.
I will keep in that mind