Given only the tags of one slice, you have to use SliceThickness
as the third dimension, though I would advice against this, as this is not guaranteed to give the distance between slices. There is the tag SpacingBetweenSlices
that provides this information, though it seems not to be present in your case.
The best way is to use the difference in ImagePositionPatient
between adjacent slices. For this, you need of course the tag of the next slice, additionally. As a side note: in your listing, ImageOrientation
and ImagePosition
should better read ImageOrientationPatient
and ImagePositionPatient
, as ImageOrientation
and ImagePosition
are other tags (not present in CT images).
ImagePositionPatient
gives the position of the upper left hand corner of the slice in DICOM patient coordinates, and to calculate the distance you have to take into account the orientation of the slice in that coordinate system. This is given by ImageOrientationPatient
, which contains the normalized rows and columns direction cosine vectors of the slices in DICOM coordinates. You can read that up in the DICOM standard.
The first two components of the orientation matrix is provided by ImageOrientationPatient
(e.g. the first and second three numbers), the third component can be calculated by taking the cross product of these 2 components.
So, in pseudo code this will look something like this:
orient1 = vector(ImageOrientationPatient[0], ImageOrientationPatient[1], ImageOrientationPatient[2])
orient2 = vector(ImageOrientationPatient[3], ImageOrientationPatient[4], ImageOrientationPatient[5])
orient3 = orient1 x orient2 // cross product
orient_matrix = matrix(orient1, orient2, orient3)
pos1 = vector(ImagePositionPatient[0], ImagePositionPatient[1], ImagePositionPatient[2]) // from current slice
pos2 = vector(ImagePositionPatient[0], ImagePositionPatient[1], ImagePositionPatient[2]) // from adjacent slice
diff_pos = pos2 - pos1
image_pos = orient_matrix o diff_pos / length(orient3) // normalized dot product
voxel_z = image_pos.z
Update: As pointed out by @gofal, the first version was incorrect. I also included the normalization (e.g. delete by length(orient3)
), though strictly speaking the values should already be normalized.