13
$\begingroup$

I have some MATLAB image data with the following dimensions (output is from MATLAB):

>> size(im)
ans =
    86    86     3    45

The data imports just fine in Mathematica, except the dimensions are reversed, and there's one extra dimension:

In[179]:= i = Import["Attractims.mat"];
          Dimensions[i]

Out[180]= {1, 45, 3, 86, 86}

It's easy enough to throw out that first dimension. But how can I massage the list to produce one whose dimensions are in the same order as in MATLAB? In other words, I want to reverse the order of the dimensions in a multi-dimensional list. At first this seemed like a trivial problem, but when I sat down to do it I found that I couldn't. Help?

EDIT:

If you want to check out the MATLAB file yourself, you can get it (for the time being) at:

[ edit: resource no longer available. ]

As I mentioned below in comments, I was unable to get Leonid's approach to change the dimensions of the imported data. However, R.M.'s approach almost works: the dimensions of the array are changed appropriately, but the X and Y are reversed. To see what I mean, download and import the data above using something like:

In[340]:= mma = Import["/wherever/Attractims.mat"];
In[341]:= mma2mat = Flatten[mma, Table[{i}, {i, Depth[mma] - 1, 1, -1}]];
In[342]:= Dimensions[mma2mat]
Out[342]= {86, 86, 3, 45, 1}

So far so good. But if you do:

Image[mma2mat[[All, All, All, 45, 1]], "byte"]

you can see that rows and columns have been transposed. I'm having a hard time wrapping my head around this thing, but if this produces the 'correct' image in MATLAB:

image(im(:,:,:,45))

then shouldn't the converted version do the same? In any event, thanks to your collective help I'm able to do the work that needs doing, but it would be nice to understand if I could...

$\endgroup$
7
  • 3
    $\begingroup$ Have a look at Transpose. It can handle multi-dimensional arrays. $\endgroup$ Sep 13, 2012 at 19:29
  • $\begingroup$ Can you give a link to your .MAT file? $\endgroup$ Sep 13, 2012 at 19:30
  • 1
    $\begingroup$ Use Reverse if it is a single vector $\endgroup$ Sep 13, 2012 at 19:34
  • $\begingroup$ If you use Flatten you can drop the first dimension and transpose at the same time. $\endgroup$ Sep 13, 2012 at 20:13
  • $\begingroup$ The problem of transposed columns and rows is because the file was originally saved using MATLAB 5 format, which has a slightly different structure. Try resaving it in MATLAB using the newer HDF5 format using the -v7.3 switch (see my answer). If you do this, then it gives you an upright image. $\endgroup$
    – rm -rf
    Sep 14, 2012 at 1:51

2 Answers 2

14
$\begingroup$

Leonid has given you the theory behind why the dimensions get flipped — it's because of how arrays are indexed. However, I offer a much simpler way of doing the transformation using the powerful second argument of Flatten. First, let's create an example in MATLAB:

mat = reshape(magic(32),[1,2,4,8,16]);
size(mat)
% ans = 1 2 4 8 16

save('~/test.mat','mat','-v7.3')

Now we import this in Mathematica

mma = Import["~/test.mat", {"HDF5", "Datasets", "mat"}];
Dimensions@mma
(* {16, 8, 4, 2, 1} *)

Ok, so to convert this to MATLAB, the transformation is as simple as the following:

mma2mat = Flatten[mma, Table[{i}, {i, Depth[mma] - 1, 1, -1}]];

The above is a generalized transpose of the list and see Leonid's excellent answer for an understanding of the second argument of Flatten.


Verification:

You can check that the results are the same by comparing slices of the array in both MATLAB and Mathematica:

In MATLAB:

squeeze(mat(1,1,:,1,1))'
% ans = 1024 65 896 193

In Mathematica:

mma2mat[[1, 1, ;; , 1, 1]]
(* {1024., 65., 896., 193.} *)
$\endgroup$
10
  • $\begingroup$ +1. I was expecting that there are simpler ways of doing that. However, the focus of the question was on the inverse - to get stuff from Matlab to mma, so I just gave the direct conversion for completeness (not that I say that there isn't a simpler way for the reverse transform either). $\endgroup$ Sep 13, 2012 at 22:09
  • $\begingroup$ @LeonidShifrin The inverse of what I've written? My answer also shows how to convert from MATLAB to Mathematica... The reverse would be as simple as reversing the order in Flatten $\endgroup$
    – rm -rf
    Sep 13, 2012 at 22:13
  • $\begingroup$ Actually, I had in mind equivalence in the sense of linear indexing. So, Flatten[list] in mma produces the same flat list as when you send flat data for fromMmaToMtlb[list] and restore the correct Matlab dimensions, and then flatten your result in Matlab via linear indexing. Your code produces a different list from my fromMmaToMtlb, so you probably had something else in mind. A pity I can't check in Matlab now. $\endgroup$ Sep 13, 2012 at 22:21
  • $\begingroup$ @LeonidShifrin My answer above is related to your fromMtlbToMma, but yes, there are differences — I don't think yours gives the correct transformation. Specifically, if you use your function on my example above, it returns a list of dimension {16, 8, 4, 1, 2} (the last two are flipped). Similarly, if you try fromMmaToMtlb, you get a list of {1, 2, 4, 16, 8} whereas I started with an array of [1, 2, 4, 8, 16] (again, last two are flipped). I think this is a small issue with note keeping, but I haven't looked through the logic of yours in detail. $\endgroup$
    – rm -rf
    Sep 13, 2012 at 22:33
  • $\begingroup$ Mine arose from the necessity of 2-way exchange between Mathematica and Matlab via Matlab C API. That API asks you to provide a flat list of data and a list of dimensions according to Matlab conventions (size), and constructs an array from that data. Complementary API functions do the reverse, allowing you to extract those components. When I was doing this for multidimensional arrays, I found the recipe I posted (otherwise data did not match the indexing). I don't now remember the details alas, and right now can not check, but we can return to this later if you wish. $\endgroup$ Sep 13, 2012 at 22:37
14
$\begingroup$

Some theory

This is not completely trivial, and the reason is in the differences between how Matlab and Mathematica represent tensors (multi-dimensional arrays), of which I will stress three:

  1. Matrices in Matlab are stored in the column-major order (like in Fortran and R), while in Mathematica they are stored in the row-major order (like in C). This is also true for sparse matrices. This has a number of implications for things like data transfer between Matlab and Mathematica (when Matlab engine C API is used), but also for linear indexing. For example,

    mlbmat = [[1 2 3]; [4 5 6]]
    mmamat = {{1,2,3},{4,5,6}}
    
    mlbmat(4) --> 5
    Flatten[mmamat][[4]] --> 4
    
  2. So called trailing (thanks for the correction, @R.M) singleton dimensions - trailing meaning dimension - 1 at the start or end of the dimensions list, but not in the middle - are automatically removed by Matlab, while they are kept in Mathematica. So, for example,

    mlbsingletons = [[[[1],[2],[3]];[[4],[5],[6]]]]
    

    is equivalent to

        [[1 2 3];[4 5 6]]
    

    while in Mathematica this would be

    {{{{1},{2},{3}},{{4},{5},{6}}}}
    

    and these "singleton" dimensions will be kept by the system.

  3. Arrays of higher dimensionality are treated also differently. Higher dimensions are added in Matlab via the pointer mechanism, so they are prepended to the list of dimensions, rather than appended to it.

Translation

I happened to have developed the translation functions in the past, so here I will post and illustrate what I was using. I won't discuss the singleton dimension, just drop it. Here are the conversion functions:

ClearAll[newDims, fromMmaToMtlb, fromMtlbToMma];
newDims[tensor_] := 
   Join[Take[#, -2], Drop[#, -2]] &@Dimensions[tensor];

fromMmaToMtlb[tensor_] := Map[Transpose, tensor, {-3}];

fromMtlbToMma[tensor_] :=
  With[{values = Flatten[tensor], dims = Reverse[Dimensions[tensor]]},
     Map[Transpose, First@Fold[Partition, values, dims], {-3}]];

I used these to communicate with Matlab via its C engine API. If you start e.g. with the following array:

tst  = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, 
       {{13,14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}};

which has dimensions

Dimensions@tst

(*  {4,3,2}   *)

then here is what is the equivalent Matlab array:

mlbtst = fromMmaToMtlb[tst]

(*  
    {{{1,3,5},{2,4,6}},{{7,9,11},{8,10,12}},
    {{13,15,17},{14,16,18}},{{19,21,23},{20,22,24}}}
*)

with (Matlab) dimensions

newDims[tst]

(*  {3,2,4}  *)

which are the reverse of Dimensions[mlbtst], due to the column-major order vs row-major order difference. Now, the reverse would be:

fromMtlbToMma[mlbtst]

(*
      {{{1,2},{3,4},{5,6}},{{7,8},{9,10},{11,12}},
      {{13,14},{15,16},{17,18}},{{19,20},{21,22},{23,24}}}
*) 

so we get back our original array.

$\endgroup$
11
  • $\begingroup$ Nice explanation, but I wouldn't say that all singleton dimensions are automatically removed — only trailing singleton dimensions are automatically removed, which is why writing something as [[[1],[2],[3]]] gives a false sense of depth (i.e., you can never equate it to Mathematica's lists). It is indeed possible to have non-trailing singleton dimensions. For example: x = reshape(magic(4),[1,4,4]); size(x) $\endgroup$
    – rm -rf
    Sep 13, 2012 at 21:43
  • $\begingroup$ @R.M. Yes, you are right, thanks. I actually meant the trailing ones. Those in the middle are not removed. Will edit the post. As a matter of fact, this Matlab's behavior, among other things, makes it difficult to write, say, a translator from Matlab to Mathematica (for example :-)). $\endgroup$ Sep 13, 2012 at 21:49
  • $\begingroup$ Re: the edit, it's only dimensions at the end of the dimensions list... those at the start are also retained, like in my example above $\endgroup$
    – rm -rf
    Sep 13, 2012 at 21:55
  • $\begingroup$ @R.M I can't launch Matlab now for some reason, so can not test, but I used to think that those dimensions are removed from both ends. You are probably right, but why then in [[[[1],[2],[3]];[[4],[5],[6]]]] both outer vector brackets and inner vector brackets are removed? $\endgroup$ Sep 13, 2012 at 22:07
  • $\begingroup$ Well, my understanding is that the brackets don't really guide the dimensions at all. It just serves as a convenient container, but it's the commas/spaces and semicolons that indicate the rows/columns. You probably might have gotten used to writing it that way, but typically it is just written as [1,2,3;4,5,6]. In essence, the brackets are stripped away until only one is required to hold the contents and don't have special meaning like List does in mma. AFAIK, there is no way to enter a multiple dimension (>2) array by hand. One would need to reshape or cat it to build it. $\endgroup$
    – rm -rf
    Sep 13, 2012 at 22:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.