LPS/RAS to voxel

Hello,

I am trying to convert a set of RAS coordinates (.fcsv) exported from slicer, into voxel units. Ideally, I would like to do this on python as part of a workflow…

I’m using matmul with the coordinates [X,Y,Z] and the affine from the .nii file header of my registered image…

voxel_coords = [np.round(np.linalg.inv(affine) @ np.array(ras_coord).T) for ras_coord in ras_vals]

The coordinates are correctly spaced and seem to even be on the same plane , but there is some misalignment with the original image. Seems to be a translational problem, I’m thinking it has to do with the last row of my affine matrix, or a field of view issue? Or did I miss a step?

Thanks,
Bart

See the Python code snippet that does this [here] (Script repository — 3D Slicer documentation). If you do this outside Slicer then there will be no transformation on the volume.

Pay attention to the coordinate system used in the file. In current Slicer versions it is LPS by default, so you can directly use it with the IJK to LPS matrix that is stored in the image header. If markup points are in RAS then you need to convert.

I would not recommend using fcsv files. They are not standard CSV. Best is to save as markup json instead, as you can reead/parse the file with a single line of code and you get easy access to all point coordinates a d all metadata in a Python object. If you want to process using Excel macros (not recommended) you can save as proper CSV by exporting the markups to a table node first (in Markups module).

Hi Andras. Appreciate your help on this. I will export as .json

My markups are in LPS format, I used the inv(affine) matrix which I got from the nifti file — nib.load(‘file.nii’).affine — is this the image header matrix you are referring to? This gave me the offset result in the screenshot

Regarding the script snippet you linked me: how do I create a volumeNode object in python? I have been looking around and it looks like I may need to use slicer.ScriptedLoadableModule or slicer.utils in this step? I assume I need to pass the .nii file

It would be helpful to know volumeNode.GetRASToIJKMatrix(volumeRasToIjk)

Best,
Bart

I can also try to be more clear on what I’m trying to do here:

  • I’ve labeled a set of markup coordinates on a CT image (.nii)
  • Using the markups export file (LPS format), I’m trying to get the coordinates of the markups in the voxel space of the CT

NIFTI format is very problematic. There are two ways to define IJK to LPS transformation (sform and qform) and not always clear which one should be used and how. Also, NIFTI uses RAS internally, so depending on what library you use to read the header and what options, you might get IJK to RAS.

To eliminate one source of error, I would recommend to save as NRRD format instead.

You can get IJK from LPS in a NRRD file using Python (outside Slicer’s Python environment) as shown here.

Thanks Andras, very resourceful.

I will look into using NRRD format.

Temporarily, a simple fix was flipping the x,y coordinates from RAS to LPS to match the seega output.