Importing image from array and axis reorder

I am creating image from a numpy array, using itk-python, and I see that it reorders the array axes:

print("Array shape: "+str(scanArray.shape))
scanVolume = itk.GetImageFromArray(scanArray)
print("Image shape: "+str(scanVolume.GetLargestPossibleRegion().GetSize()))

Result:

Array shape: (512, 512, 133)
Image shape: itkSize3 ([133, 512, 512])

So I then thought I can swap axes to take care of this, but the result is not what I was expected:

print("Array shape: "+str(scanArray.shape))
scanArray = np.swapaxes(scanArray,0,2)
print("Array shape reoriented: "+str(scanArray.shape))
scanVolume = itk.GetImageFromArray(scanArray)
print("Image shape: "+str(scanVolume.GetLargestPossibleRegion().GetSize()))

Result:

Array shape: (512, 512, 133)
Array shape reoriented: (133, 512, 512)
Image shape: itkSize3 ([133, 512, 512])

I don’t understand what is happening here, and why the shape of the ITK image does not change although I change the numpy array shape.

Can someone help what is the proper way to re-shape the array here?

2 Likes

You have created a np view. Perhaps try a deep copy with numpy.copy()?

1 Like

It could be not a problem. Aren’t ITK indices [x,y,z], whereas numpy’s are [z][y][x]?

1 Like

Thank you, this solved the problem!

Right, but that reorder should apply both when numpy is [512,512,133] and [133,512,512], and it was swapping them only in one instance.

ITK indices, by convention, are [i,j,k] while NumPy indices are [k,j,i] by convention.

That is

itk_image.GetPixel([i,j,k])

is the same as

numpy_array_as_image[k,j,i]

With ITK 4.13.1, the keepAxes keyword argument to GetArrayFromImage can be used to reorder the array; in ITK 5, this will be keep_axes as we become more PEP8 compliant / Pythonic with the Python API. As noted in the documentation, the numpy operator here for GetImageFromArray to flip the image is numpy.reshape:

However, it is strongly recommended not to do this, because images will be flipped in matplotlib, etc. and it has memory usage and performance downsides.

Not available yet with pip install - I get 4.13.0.

I have another beginner question - what is the documentation location for itk-python? I googled for the documentation for GetImageFromArray, but could only find pointers to examples and mailing list posts. I now also searched for it on ITK doxygen, and did not find it there either.

Sorry, another question - if it is strongly not recommended to use that parameter to flip the image, then what is the recommended approach to re-orient/reshape/re-flip the image so that it has proper orientation when saved in an ITK-readable volumetric format?

Do not reshape the image :wink: – use ITK and Python indexing conventions as appropriate.

Sorry, this is coming, “soon” :clock4:.

These are all good questions, @fedorov .

Google is not too helpful with documentation pointers, yet. :frowning:

For GetImageFromArray, the most convenient way to get help is

help(itk.GetImageFromArray)

or, in IPython / Jupyter

itk.GetImageFromArray?

There is also the ITK Python Quick Start Guide and the Python section of the ITK Software Guide.

1 Like

Hmmm … Ok, let me give you a bit more details on what I need to do then!

I have an image (CT series) coming from DICOM. I have the segmentation of that CT series coming from another python-based tool. I want the two to line up in Slicer.

I think “do not reshape the image” is not the answer I am looking for here! :wink:

1 Like

In case it can help someone later, I was able to recover proper orientation of the volume saved by ITK with the following 2 operators:

scanArray = np.swapaxes(scanArray,0,2).copy()
scanArray = np.rollaxis(scanArray,2,1).copy()
2 Likes

Thanks for sharing the solution.

Another option:

scanArray = np.transpose(scanArray, (2,1,0))

Which Python-based tool is this?

I am working on a project to generate standard DICOM representation for the XML annotations in this collection: https://wiki.cancerimagingarchive.net/display/Public/LIDC-IDRI. Those XML files are already parsed by this library: https://pylidc.github.io/, so I am using the numpy masks of the segmentation as the input for the conversion process with itk-python.

2 Likes

I’ve come across another particular issue regarding how the data is read in python ITK.
I’m using ITK to read image and store them in the tensorflow friendly format (tfrecords).
I have trained a network using these and everything seems fine while working in python.

My issue starts when I export my trained model for deployment. I want to use the trained model in an application using itk.js. When trying to do inference using this model, the output data seems to have issues with the underlying data organization.

This is the output of the inference function in javascript

The image looks like this after inference in python

Any ideas on how this should be handled?

Thanks!

I figured out a way to make it work. Since itk.js and python itk have the same underlying data organization, the trick is to flip the dimensions when feeding the data to the tensor.
Here is an example:

const tf = require('@tensorflow/tfjs-node');
const readImageLocalFileSync = require('itk/readImageLocalFileSync');
const writeImageLocalFileSync = require('itk/writeImageLocalFileSync');


var inputFileName = './myImg.nrrd';
var outputFileName = './myImgOut.nrrd';
var inputModelDir = './myExportedModelDir';
const useCompression = true

tf.node.loadSavedModel(inputModelDir)
.then(function(model){

    const in_img = readImageLocalFileSync(inputFileName);
    var img_size = [...in_img.size];//Copy the dimensions to a new array
    img_size.reverse();//Reverse the dimensions array
    var x = tf.tensor(Float32Array.from(in_img.data), [1, ...img_size, in_img.imageType.components]);//Reshape the tensor using the reversed array
    
    var y = model.predict(x);

    const out_img = new Image(new ImageType(2,'float'));
    out_img.origin = in_img.origin;
    out_img.spacing = in_img.spacing;
    out_img.direction = in_img.direction;
    out_img.size = in_img.size;

    return y.buffer()
    .then(function(buf){
        out_img.data = buf.values;
        writeImageLocalFileSync(useCompression, out_img, outputFileName)
    });
})
.catch(function(err){
    console.error(err);
})
2 Likes