Set Image Direction from numpy array

python

#1

Hello,

I’m struggling to use a numpy array in order to set the direction of an ITK Image using python bindings.

My attempts:

In [8]: image = itk.Image.New()

In [9]: np_dir = np.eye(2)

In [10]: image.SetDirection(itk.GetVnlMatrixFromArray(np_dir))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-7b1c85a3c519> in <module>()
----> 1 image.SetDirection(itk.GetVnlMatrixFromArray(np_dir))

TypeError: in method 'itkImageBase2_SetDirection', argument 2 of type 'itkMatrixD22 const &'

okay, so you want an itkMatrixD22, not a VnlMatrix.

Let’s try that:

In [11]: itk_dir = itk.Matrix[itk.D, 2, 2]

In [12]: dir(itk_dir)
Out[12]: 
[[...]
 'GetVnlMatrix',
 [...]]

No SetVnlMatrix (I would have liked that) but maybe I can GetVnlMatrix and then use this object to set the value…

In [13]: itk_dir.GetVnlMatrix()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-13-83a493e54f83> in <module>()
----> 1 itk_dir.GetVnlMatrix()

NotImplementedError: Wrong number or type of arguments for overloaded function 'itkMatrixD22_GetVnlMatrix'.
  Possible C/C++ prototypes are:
    itkMatrixD22::GetVnlMatrix()
    itkMatrixD22::GetVnlMatrix() const

How do I set the values of my itkMatrixD22? I couldn’t find. :frowning:

I may be in a wrong direction, but as my post’s title states, I want to use a python object (a numpy array, but a tuple or a list are fine too) to set the direction matrix of my image. How do I do that?

Thanks


(Matt McCormick) #2

Hello @nicoco,

It is a little tricky. Here are the calls:

np_dir_vnl = itk.GetVnlMatrixFromArray(np_dir)
direction = image.GetDirection()
direction.GetVnlMatrix().copy_in(np_dir_vnl.data_block())

HTH,
Matt


#3

Thank you!

This is exactly what I needed and it is indeed a little tricky.


(Thomas Janvier) #4

Hi,

Having some trouble getting the VnlMatrix from a numpy array

I perform a PCA on the ‘positive’ pixels coordinates to approximate the ‘main axes’ ahead of a rotation. However, I got errors casting the Numpy.array to VnlMatrix:

In [56]: eigenVectors
Out[56]:
array([[ 0.        ,  1.        , -0.        ],
       [-0.70710678,  0.        , -0.70710678],
       [ 0.70710678,  0.        , -0.70710678]])

In [57]: rotationMatrix = itk.GetVnlMatrixFromArray(eigenVectors)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
RuntimeError: Cannot get an instance of NumPy array.

During handling of the above exception, another exception occurred:

SystemError                               Traceback (most recent call last)
<ipython-input-57-40745ee4ad3d> in <module>()
----> 1 rotationMatrix = itk.GetVnlMatrixFromArray(eigenVectors)

~/anaconda3/lib/python3.5/site-packages/itkExtras.py in GetVnlMatrixFromArray(arr)
    336     """Get a vnl matrix from a Python array.
    337     """
--> 338     return _GetVnlObjectFromArray(arr, "GetVnlMatrixFromArray")
    339
    340 # return an image

~/anaconda3/lib/python3.5/site-packages/itkExtras.py in _GetVnlObjectFromArray(arr, function)
    326     PixelType = _get_itk_pixelid(arr)
    327     templatedFunction = getattr(itk.PyVnl[PixelType], function)
--> 328     return templatedFunction(arr)
    329
    330 def GetVnlVectorFromArray(arr):

~/anaconda3/lib/python3.5/site-packages/itk/Configuration/../itkPyBufferPython.py in GetVnlMatrixFromArray(ndarr)
   6506             "Only arrays of 2 dimensions are supported."
   6507
-> 6508         mat = itkPyVnlD._GetVnlMatrixFromArray( ndarr, ndarr.shape)
   6509
   6510         return mat

~/anaconda3/lib/python3.5/site-packages/itk/Configuration/../itkPyBufferPython.py in _GetVnlMatrixFromArray(arr, shape)
   6391     def _GetVnlMatrixFromArray(arr: 'PyObject *', shape: 'PyObject *') -> "vnl_matrixD const":
   6392         """_GetVnlMatrixFromArray(PyObject * arr, PyObject * shape) -> vnl_matrixD"""
-> 6393         return _itkPyBufferPython.itkPyVnlD__GetVnlMatrixFromArray(arr, shape)
   6394
   6395     _GetVnlMatrixFromArray = staticmethod(_GetVnlMatrixFromArray)

SystemError: <built-in function itkPyVnlD__GetVnlMatrixFromArray> returned a result with an error set

(Matt McCormick) #5

@T4mmi could you please save eigenVectors and share them?


(Thomas Janvier) #6

Here is the matrix

eigenVectors.npy (200 Bytes)

I obtain it from the following :

# Main axes computation
objectImageFilter.Update()
mainConnComp = itk.GetArrayViewFromImage(objectImageFilter.GetOutput())
pointsCoords = numpy.asarray(numpy.where(mainConnComp)).T
# rotationCenter = [round(i/2) for i in mainConnComp.shape]  # rotate around the image center
rotationCenter = [int(i) for i in numpy.mean(
    pointsCoords, axis=0)]  # rotate around the object center
covarianceMatrix = numpy.cov(pointsCoords, rowvar=False)
pointsCoords = None
eigenValues, eigenVectors = numpy.linalg.eigh(covarianceMatrix)
covarianceMatrix = None
descendantIndices = numpy.argsort(eigenValues)[::-1]
eigenVectors = eigenVectors[:, descendantIndices] 

(Matt McCormick) #7

Thanks @T4mmi.

It looks like, at least for now, itk.GetVnlMatrixFromArray requires a C-order array and does not handle a Fortran-order array. We will work on fixing that, or at least improving the error message.

To convert it, try:

rotationMatrix = itk.GetVnlMatrixFromArray(np.ascontiguousarray(eigenVectors))

(Thomas Janvier) #8

Well, again thanks a lot


(Simon Rit) #9

Hi,
I tried to reproduce this code but I get
AttributeError: ‘SwigPyObject’ object has no attribute ‘copy_in’
with the master HEAD. Has something changed? Is there any other way to modify the values of an itk.Matrix than to use a numpy array?
Thanks,
Simon


(Francois Budin) #10

There is currently no easy way to change the values in an ITK matrix. You can query the value with [ ], but to change the value you have to use a VNL matrix. That could be improved…
I haven’t tried yet to reproduce your error with master, but in theory nothing should have changed.


(Simon Rit) #11

Thanks. Actually, I had wrapped new itk::Matrix templates but I had not wrapped the corresponding vnl_matrix_fixed. Now that this is fixed, I can easily change the values using (here with a matrix wrapped by default),

>>> import itk
>>> m = itk.Matrix[itk.D,3,3]()
>>> print(m(2,1))
0.0
>>> m.GetVnlMatrix().put(2,1,3.14)
>>> print(m(2,1))
3.14


(Simon Rit) #12

Be aware that my previous solution is no longer valid after this commit (after version 5rc01):


If you want to modify an ITK matrix from python, the simplest in my opinion is to go to NumPy with itk.GetArrayFromVnlMatrix and itk.GetVnlMatrixFromArray (or soon with GetArrayFromMatrix and GetMatrixFromArray see

).


(Zhuangming Shen) #13

Hi simon.rit,

I found m.GetVnlMatrix().set(2, 1, 3.14) can assign value to the matrix as well. What is difference between “set” and “put”?

Regards,

Zhuangming Shen


(Simon Rit) #14

They both do the same job but set returns a reference to the matrix. See the code here. I’m not sure when set would be useful to be honest. Both put and set won’t change the elements of the itk.Matrix after this commit.


(Zhuangming Shen) #15

Thanks for your explanation.