Search code examples
matlabindexingmasksubscriptroi

Converting subsripts and indices


I'm having a hard time figuring out transformation between spaces using ind2sub and sub2ind. Could someone help? The problem is as follows:

I have a mask Y (or region-of-interest) in which voxel values are either 1 or zero: Y=72x72x33 double. I then find all the voxels with value of 1 (there are 15 of them) then use ind2sub to get the x y z coordinates for these voxels:

indx = find(Y>0);
[x,y,z] = ind2sub(size(Y),indx); 
XYZ = [x y z]'; 

Since there are 15 voxels with the value of 1, I end up with XYZ=3x15 double, containing coordinates of these 15 voxels, something like this:

25 26 24 25 26 ...26
28 28 29 29 29 ...30
8  8  8  8  8  ...9

Based on some arbitrary criteria, I remove 6 voxels so XYZ become 3x9 double. Let's call this new_XYZ. Now I would like to transform this new_XYZ back into a mask (let's call it new_Y). I tried this:

new_Y=sub2ind(size(Y),new_XYZ);

Here, I probably did something wrong with the sub2ind since new_Y didn't give me what I expected. The dimensions are also not 72x72x33. The old mask is a sphere so I expect the new mask to be close to that. Instead, I get a straight line. Can someone help me with the transformation?

Thanks.

A.


Solution

  • There are a couple of things wrong in your approach. To detail I'll use a slightly smaller sample data set, but the explanation can scale up to any size.

    Y = randi(10,5,5,3)-5 ;
    

    This create a 5x5x3 containing random integers numbers from -5 to 5 (so a good chance to have about half of them positive (>0).

    Before I go further, you can get a direct mask of your condition in the same shape of your matrix:

    mask_positive_Y = Y>0 ;
    
    >> whos mask_positive_Y
      Name                 Size             Bytes  Class      Attributes
      mask_positive_Y      5x5x3               75  logical
    

    This gives you an array of logical (boolean), the same size of your matrix, containing 0 (false) everywhere and 1 (true) where your condition is validated (Y>0).

    If you can work directly with this mask, you do not need to use ind2sub and sub2ind back and forth. For example, doing Y(mask_positive_Y) = NaN; would replace all the values indicated by the mask with NaN.

    Since you want to modify the mask (remove some points), you may still need to get their indices, in which case you can simply call:

    indx = find( mask_positive_Y ) ; %// will give the same result as: indx = find(Y>0);
    

    Now let's assume you got your indices the way you specified, removed the 6 points and got your new_XYZ matrice. The way to rebuild the mask is as follow.

    ind2sub gave you 3 vectors as output, and right enough, sub2ind will expect 3 vectors as input (not a 3 column matrix as you did). So you will have to decompose your new_XYZ into 3 vectors before you send it to sub2ind.

    new_indx = sub2ind(size(Y), new_XYZ(:,1) , new_XYZ(:,2) , new_XYZ(:,3) );
    

    But don't forget that you did transpose your result matrix when you did XYZ = [x y z]';, so make sure your new_XYZ is also transposed back with new_XYZ = new_XYZ.' ; before you decompose it in the code line above (or simply send the lines of new_XYZ instead of the columns as showed).

    By the way, the proper transpose shorthand notation is .' (and not simply ' which is the Complex conjugate transpose).

    Now this new_indx is only a new vector of linear indices, homogeneous to the indx you had earlier. You could already use this to assign values under the mask, but if you want a new mask the same shape than your matrix Y, you have to go a bit further:

    new_Ymask = false( size(Y) ) ;  %// build an empty mask (false everywhere)
    new_Ymask(new_indx) = true ;    %// assign true to the masked values
    

    This will be the same size as your initial matrix Y, but also the same size as the first boolean mask I showed you mask_positive_Y.