简体   繁体   中英

Transferring matlab 3D-matrices to python 3D-arrays via scipy.io.loadmat is going wrong

I'm considering a problem with Matlab matrices and Python numpy arrays. I have a Code for Medical image processing, given a 3D array as input (512x512 images, 80 slices -> size(image) = (512,512,80). The input is originally given by a NifTI-structure. When I load the Nii-file in matlab, i get 512x512x80 as size. When I load the Nii-file in python, i get 80x512x512 as size (with array.shape)

With trying out testmatrices, i found out: Matlabs syntax for size: (x = rows, y = columns, z = slices in 3rd dimenson) python syntax for size: (z = slices in 3rd dim, x = rows, y = columns)

Now I have to test another data, not given as nii-file, but saved in mat-files. With Scipy's loadmat I can load the matrix easily into my python program. But: Now the matrix' size is now (512,512,80) in python as well and the following code now takes 512 slices in the 3rd dimension, 512 rows and 80 columns (instead of 80 slices, 512x512 images). I hope this is understandable explanation...

A minimal example:

I load a Matrix in Matlab by:

a = 1:12;
a = reshape(a, [1 3 4])

a(:,:,1) =

     1     2     3


a(:,:,2) =

     4     5     6


a(:,:,3) =

     7     8     9


a(:,:,4) =

    10    11    12

size(a)

ans =

     1     3     4

With

save a.mat a

I save a in a mat file. Now I go on to my python code, using import scipy.io as spio I can access the matfile:

mat = spio.loadmat('a.mat')
a = mat['a']
print(a)

   Output:
[[[1 4 7 10]

  [2 5 8 11]

  [3 6 9 12]]]

print(a.shape)

   Output:
(1,3,4)

But this is wrong! It has no 4 slices in 3rd dimension, but rather is just a 3x4 matrix. I should be:

[[[ 1 2 3]]

 [[ 4 5 6]]

 [[ 7 8 9]]

 [[10 11 12]]]

with shape (4,1,3)

So, How can I load Matlab-Matrices in the exact python order, such that my code takes the right slices? Further am I interested in transferring the resulting python array back to matlab matrices again.

Thank you much in advance!

You are mistaken the visualization of matlab:

a(:,:,1) =

     1     2     3

Does not mean that the last axis of a contains 1 2 3 , it means that the group of the first elements of the second axis is 1 2 3 . Python just shows you the full array, matlab is showing you columns of that array (I call columns to the second axis assuming we ignore the first one, as is empty).

You can check that both, matlab and python loaded arrays are equivalent by typing in Python:

>>> a[:, :, 0]
array([[1, 2, 3]], dtype=uint8)

It gives you the same result than matlab's visualization.

Python and matlab just have different ways of visualizing 3-dimensional arrays.


With that, note that matlab is Fortran ordered, which means that columns come first in an array, and then the rows. Thus,

matlab> reshape(1:6, [2, 3])
ans =

     1     3     5
     2     4     6

This time as the matrix only has 2 columns, the visualization is what you would expect. If you load that matrix in python it will also be properly loaded (python is smart enough to load a numpy array with Fortran ordering). However, note that when you try to create the equivalent matrix in python, the result is very different:

python> np.arange(1,7).reshape(2, 3)
array([[1, 2, 3],
       [4, 5, 6]])

The ordering is different. In numpy arrays are by default C-ordered.


Last, with any recent numpy/scipy version, then, you should be able to import/export matrices from matlab without any modification.

matlab> a = rand(512, 512, 10);
matlab> save /tmp/a.mat a

Then load it in python:

python> a = spio.loadmat('/tmp/a.mat')['a']
python> a.shape
(512, 512, 10)

The matrices are identical. Thus, there is nothing wrong with matlab -> python conversion. You just seem to want a transposed version to work with it in python.


If you want a pythonic (10, 512, 512) array, you just have to transpose it:

python> a = np.ascontiguousarray(a.T)

And if you only want to transpose second and last axis (as in your example), you would do it with swapaxes :

python> a = np.ascontiguousarray(np.swapaxes(a, 1, 2))

Note that, ascontiguousarray will copy the array (if needed) to a C-ordering contiguous block of memory. More efficient for further numpy processing routines.


To finish, an example with your dummy sample a matrix:

>>> np.swapaxes(a, 1, 2)
array([[[ 1,  2,  3],
        [ 4,  5,  6],
        [ 7,  8,  9],
        [10, 11, 12]]], dtype=uint8)

I would still wrap it with np.ascontiguousarray to make sure the data gets copied to a new block of memory!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM