Unexpected behaviour with control point coordinates outside of Slicer

I ran into a problem using a Slicer markup json file outside of Slicer. Either I am missing something or there might be a bug somewhere.

Steps to reproduce:

  • open CTchest sample image
  • make point annotations and save the control points as a mrk.json file
  • using nibabel, load the image
  • nib.aff2axcodes(img.affine) confirms image is in LPS orientation
  • load the json file and access the coordinates
  • invert the affine matrix and apply it to the coordinates to compute voxel positions in image
  • results are not as expected, e.g. values out of image size can appear (also negative values)
  • invert first two coordinates of control points
  • convert modified world coordinates to image positions as before
  • results now correspond to expected values

I have seen that the control points are saved with the header information “coordinateSystem”: “LPS” but I do not understand how this influences world coordinates in mm space. I understand that the orientation dictates how image data relate to world coordinates but does a point in mm space depend on the orientation? Shouldn’t I be able to convert these world coordinates into any image by applying the image specific affine matrix?
I wonder if slicer “converts” the LPS coordinate when loading to RAS by flipping the sign on the first two coordinates and vice versa when saving. Otherwise I don’t see how things work out within Slicer but not outside.
Please advice if I have an oversight here or the coordinates are saved as intended.

"coordinateSystem": "LPS" in the markup file means that the coordinates are specified in LPS coordinate system.

NIFTI always uses RAS as world (physical) coordinate system, regardless of the order or orientation of image axes. By inspecting aff2axcodes output, you may find that order and direction of some image axes happen to coincide with some world coordinate system axes (e.g., direction of image I, J, K axes may be the same as the L, P, S axes of the LPS coordinate system), but that does not change that NIFTI still specifies the image geometry (origin, spacing, axis directions) in the RAS coordinate system.

Yes. Coordinate values of a point depend on the orientation of the coordinate system axes.

Yes, conversion between world and voxel coordinates is a simple homogeneous transformation. For example:

Point_IJK = Transform_RASToIJK * Point_RAS
  = inv(Transform_IJKToRAS) * Point_RAS
  = inv(Transform_IJKToRAS) * Transform_LPSToRAS * Point_LPS
  = inv(Transform_IJKToRAS) * diag(-1,-1,1,1) * Point_LPS

Yes, this is exactly what happens. You can see this if you have a look at coordinate values in the Slicer GUI and in the json file. Since practically all medical image computing file formats (DICOM, NRRD, MetaIO, etc.) uses LPS coordinate system we had no other choice in Slicer but save all data in LPS coordinate system. However, Slicer internally still uses RAS coordinate system. We’ll probably switch to LPS coordinate system in the future.

Thanks a lot for this answer. I see where I got mixed up. It seems to be easiest to just convert everything to RAS while working with NIFTI images. :+1:

NIFTI files are problematic for many reasons (complicated yet limited, ambiguous image orientation definition, etc.) and RAS is only used in few applications (mostly those that were developed before LPS has become the de facto standard coordinate system in medical image computing).

So, in general, my recommendation would be to use LPS coordinate system and not to use NIFTI file format. If you use NIFTI then you can still use LPS coordinate system for all computations (if you use ITK for loading a NIFTI image then it automatically converts it to be in the LPS coordinate system).