MetaIO image rotation conventions

For the TransformMatrix / Orientation / Rotation tags - which are storing the image rotation in the MetaIO (MHD/MHA) format - the actual serialization convention (row-major vs. column major) does not seem to be documented anywhere (at least I could not really find anything beyond https://itk.org/Wiki/ITK/MetaIO/Documentation#Associated_transformations).

As far as I could see from MetaIO/metaObject.cxx at 5e87f062ea796204b84fc3e5e9100cd3ead2e015 · Kitware/MetaIO · GitHub and MetaIO/metaObject.cxx at 5e87f062ea796204b84fc3e5e9100cd3ead2e015 · Kitware/MetaIO · GitHub the reference implementation seems to store it row-major (assuming that _i refer to rows and _j to columns respectively in TransformMatrix(int _i, int _j).

On the other hand, experiments with a mesh and an associated image (TransformMatrix = 0 0 1 1 0 0 0 1 0 - i.e. not invariant to transposition) in 3D Slicer suggest that the orientation is actually stored / interpreted as a column-major.

Finally, I actually wanted to do the same experiment with ParaView, which unfortunately seems to completely ignore the image orientation.

Would anyone be able to confirm what is the standard convention and where this is documented? I did find 22_Transforms saying that “Matrices are represented by vector-like data types in row major order” but I am unsure whether this applies for the MetaIO/MHD/MHA file format or is only internal to SimpleITK? I guess this should be highly relevant for registration topics, no?

@Stephen_Aylward might be able to answer this.

1 Like

Hello @ingmar.voigt,

The storage in the meta-io image is in column major order.

SimpleITK’s convention of representing a matrix is in row major order, so [0,0,-1,0,1,0,1,0,0] represents the matrix \left[\begin{array}{ccc} 0 & 0 & -1\\ 0 & 1 & 0 \\ 1 & 0 & 0 \end{array}\right].
Trust but verify:

import SimpleITK as sitk

image = sitk.Image([2]*3, sitk.sitkUInt8)
image.SetDirection([0,0,-1,0,1,0,1,0,0])
print(image) # look at the Direction matrix
sitk.WriteImage(image, 'image.mhd')

Content of header (.mhd) file:

ObjectType = Image
NDims = 3
BinaryData = True
BinaryDataByteOrderMSB = False
CompressedData = False
TransformMatrix = 0 0 1 0 1 0 -1 0 0
Offset = 0 0 0
CenterOfRotation = 0 0 0
AnatomicalOrientation = IAL
ElementSpacing = 1 1 1
DimSize = 2 2 2
ElementType = MET_UCHAR
ElementDataFile = image.raw
2 Likes

Hi @ingmar.voigt

Perhaps I am misreading it, but I believe that the code you cited is also column major ordering, i.e., the column variable (j) is in the inner loop. So, I think it is consistent, but you’re right that it isn’t documented. I’m adding it to the wiki page you cited.

Thanks!
Stephen

2 Likes

Great, thank you all for your responses and for extending the wiki page!

Meanwhile we had btw. also found another confirmation from earlier https://itk.org/pipermail/community/2014-October/007437.html and double checked with ITKSnap (via “Reorient image” dialog box) and 3DSlicer (loading associated mesh)

1 Like

@Stephen_Aylward just wondering: doesn’t column variable j in inner loop mean that it’s in row major order? Or is m_TransformMatrix already column major too (i.e. following he OpenGL convention I believe)?

From the signature TransformMatrix(int _i, int _j ..) I assumed _i would be rows and _j would be columns

MetaObject::TransformMatrix(int _i, int _j, double _value)
{
  m_TransformMatrix[_i * m_NDims + _j] = _value;
}

Ah - sorry for the confusion.

m_TransformMatrix just stores the serialization prior to writing it - so, yes, it too is column major.

s