How to create a displacement field image from a numpy array without files?

Continuing the discussion from Creating a displacement field image from a numpy array:

Hello!

I am trying to warp an image. The image is built from an array, and I would like to build the displacement field from another array as well.

(This post is very similar. This and this may be related). The difference is that I am trying to do this (1) without reading files and (2) setting each voxel (pixel) value with for loops (something like ImageIterator), but using pure arrays. My hair is getting loose :wink: .

import numpy as np
import itk

img_shape = (5, 101, 101)
fixed_image = itk.image_from_array(
    np.zeros(img_shape).astype(np.float32))
array_from_fixed_image = itk.array_from_image(fixed_image)

VoxelType = itk.F
ImageDimension = len(img_shape)
ImageType = itk.Image[VoxelType, ImageDimension]

VectorVoxelType = itk.F
VectorDimension = 3
VectorTypeDim = itk.Vector[VectorVoxelType, VectorDimension]
VectorImageType = itk.Image[VectorTypeDim, ImageDimension]
DisplacementField = itk.image_from_array(
    np.ones((3, *array_from_fixed_image.shape),
            dtype=np.float32),
    is_vector=True)

interpolator = itk.LinearInterpolateImageFunction[
    ImageType, itk.D].New()

# This statement failed
warpFilter = itk.WarpImageFilter[
    ImageType, ImageType, itk.Image[itk.F,4]].New(
    Interpolator=interpolator,
    OutputSpacing=fixed_image.GetSpacing(),
    OutputOrigin=fixed_image.GetOrigin(),
    DisplacementField=DisplacementField,
    Input=fixed_image)
moving_image = warpFilter.GetOutput()

The error that I get is:

>>>
Traceback (most recent call last):
  File "/home/edgar/.local/lib/python3.10/site-packages/itk/support/template_class.py", line 525, in __getitem__
    this_item = self.__template__[key]
KeyError: (<class 'itk.itkImagePython.itkImageF3'>, <class 'itk.itkImagePython.itkImageF3'>, <class 'itk.itkImagePython.itkImageF4'>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 17, in __PYTHON_EL_eval
  File "<string>", line 26, in <module>
  File "/home/edgar/.local/lib/python3.10/site-packages/itk/support/template_class.py", line 529, in __getitem__
    raise itk.TemplateTypeError(self, key)
itk.support.extras.TemplateTypeError: itk.WarpImageFilter is not wrapped for input type `itk.Image[itk.F,3], itk.Image[itk.F,3], itk.Image[itk.F,4]`.

To limit the size of the package, only a limited number of
types are available in ITK Python. To print the supported
types, run the following command in your python environment:

    itk.WarpImageFilter.GetTypes()

Possible solutions:
* If you are an application user:
** Convert your input image into a supported format (see below).
** Contact developer to report the issue.
* If you are an application developer, force input images to be
loaded in a supported pixel type.

    e.g.: instance = itk.WarpImageFilter[itk.Image[itk.SS,2], itk.Image[itk.SS,2], itk.Image[itk.Vector[itk.F,2],2]].New(my_input)

* (Advanced) If you are an application developer, build ITK Python yourself and
turned to `ON` the corresponding CMake option to wrap the pixel type or image
dimension you need. When configuring ITK with CMake, you can set
`ITK_WRAP_${type}` (replace ${type} with appropriate pixel type such as
`double`). If you need to support images with 4 or 5 dimensions, you can add
these dimensions to the list of dimensions in the CMake variable
`ITK_WRAP_IMAGE_DIMS`.

Supported input types:

itk.Image[itk.SS,2]
itk.Image[itk.UC,2]
itk.Image[itk.US,2]
itk.Image[itk.F,2]
itk.Image[itk.D,2]
itk.Image[itk.SS,3]
itk.Image[itk.UC,3]
itk.Image[itk.US,3]
itk.Image[itk.F,3]
itk.Image[itk.D,3]
itk.Image[itk.SS,4]
itk.Image[itk.UC,4]
itk.Image[itk.US,4]
itk.Image[itk.F,4]
itk.Image[itk.D,4]

May be related to Creating a displacement field image from a numpy array - #11 by Alex_Reimelt

    # create_displ.py creates a displacement field with ITK
    # warping does not work as expected
    #
    # Copyright (C) 2023  eDgar
    #
    # This program is free software: you can redistribute it
    # and/or modify it under the terms of the GNU General Public
    # License as published by the Free Software Foundation,
    # version 3 of the License.
    #
    # This program is distributed in the hope that it will be
    # useful, but WITHOUT ANY WARRANTY; without even the implied
    # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    # PURPOSE.  See the GNU General Public License for more
    # details.
    #
    # You should have received a copy of the GNU General Public
    # License along with this program.  If not, see
    # <http://www.gnu.org/licenses/>.
    
    
    import itk
    import numpy as np
    
    ndim = 3
    nz = 4
    ny = 30
    nx = 20
    
    img_shape = (nz, ny, nx)
    img = np.zeros(img_shape)
    img[:,
        (ny // 3):(ny * 2 // 3),
        (nx // 4):(nx * 3 // 4)] = 1
    # import matplotlib.pyplot as plt
    # plt.imshow(img[0])
    # plt.show()
    fixed_image = itk.image_from_array(
        img.astype(np.float32))
    
    VoxelType = itk.F
    ImageType = itk.Image[VoxelType, ndim]
    
    VectorVoxelType = itk.F
    VectorDimension = ndim
    VectorTypeDim = itk.Vector[VectorVoxelType, VectorDimension]
    ImageDimension = ndim
    VectorImageType = itk.Image[VectorTypeDim, ImageDimension]
    
    # Unitary displacement field (translate)
    DisplacementFieldNumpy = np.ones(
        (nz, ny, nx, ndim), dtype=np.float32)
    # Old-school
    PyBuffer = itk.PyBuffer[VectorImageType]
    # DisplacementField = PyBuffer.GetImageFromArray(
    #     DisplacementFieldNumpy.astype(np.float32))
    DisplacementField = PyBuffer.GetImageViewFromArray(
        DisplacementFieldNumpy, is_vector=True)
    DisplacementField.shape
    DisplacementField.SetSpacing(fixed_image.GetSpacing())
    DisplacementField.SetOrigin(fixed_image.GetOrigin())
    # itk.array_from_image(DisplacementField).shape
    
    DisplacementField = itk.image_view_from_array(
        DisplacementFieldNumpy,
        # is_vector=True
        ttype=VectorImageType
    )
    DisplacementField.SetSpacing(fixed_image.GetSpacing())
    DisplacementField.SetOrigin(fixed_image.GetOrigin())
    
    interpolator = itk.LinearInterpolateImageFunction[ImageType, itk.D].New()
    
    warpFilter = itk.WarpImageFilter[ImageType, ImageType, VectorImageType].New(
        Interpolator=interpolator,
        OutputSpacing=fixed_image.GetSpacing(),
        OutputOrigin=fixed_image.GetOrigin(),
        DisplacementField=DisplacementField,
        Input=fixed_image.astype(itk.F))
    moving_image = warpFilter.GetOutput()
    itk.imwrite(DisplacementField, "/tmp/displ_field_by_itk.vtk")
    itk.imwrite(moving_image, "/tmp/warped_by_itk.vtk")
    itk.imwrite(fixed_image, "/tmp/fixed_image_by_itk.vtk")