Search code examples
pythonnumpymaskquaternionsscikit-image

3d coordinates x,y,z to 3d numpy array


I have a 3d mask which is an ellipsoid. I have extracted the coordinates of the mask using np.argwhere. The coordinates can be assigned as x, y, z as in the example code. My question is how can I get my mask back (in the form of 3d numpy or boolean array of the same shape) from the coordinates x, y, z ?

import numpy as np
import scipy
import skimage
from skimage import draw

mask = skimage.draw.ellipsoid(10,12,18)
print mask.shape 

coord = np.argwhere(mask)

x = coord[:,0]
y = coord[:,1]
z = coord[:,2]

The above code gives me boolean mask of the shape (23, 27, 39) and now I want to construct the same mask of exactly same shape using x, y, z coordinates. How can it be done?

I would like to modify the question above a bit. Now if I rotate my coordinates using quaternion which will give me new set of coordinates and then with new coordinates x1,y1,z1 I want to construct my boolean mask of shape (23,27,39) as that of original mask ? How can that be done ?

import quaternion
angle1 = 90
rotation = np.exp(quaternion.quaternion(0,0, 1) * angle1*(np.pi/180) / 2)
coord_rotd = quaternion.rotate_vectors(rotation, coord)
x1 = coord_rotd[:,0]
y1 = coord_rotd[:,1]
z1 = coord_rotd[:,2]

Solution

  • You can use directly x, y and z to reconstruct your mask. First, use a new array with the same shape as your mask. I pre-filled everything with zeros (i.e. False). Next, set each coordinate defined by x, y and z to True:

    new_mask = np.zeros_like(mask)
    new_mask[x,y,z] = True
    
    # Check if mask and new_mask is the same
    np.allclose(mask, new_mask)
    # True
    

    If you ask, if you can reconstruct your mask only knowing x, y and z, this is not possible. Because you loose information of what is not filled. Just imagine having your ellipsoid at a corner of a huge cube. How would you know (only knowing how the ellipsoid looks), how large the cube is?

    Regarding your second question:

    You have to fix your coordinates, because they can be out of your scenery. So I defined a function that takes care of this:

    def fixCoordinates(coord, shape):
        # move to the positive edge
        #  remove negative indices
        #  you can also add now +1 to 
        #  have a margin around your ellipse
        coord -= coord.min(0)
    
        # trim coordinates outside of scene
        for i, s in enumerate(shape):
            coord[coord[:,i] >= s] = s-1
    
        # Return coordinates and change dtype
        return coord.astype(np.int)
    

    And if you modify your code slightly, you can use the same strategy as before:

    # your code
    import quaternion
    angle1 = 90
    rotation = np.exp(quaternion.quaternion(0,0, 1) * angle1*(np.pi/180) / 2)
    coord_rotd = quaternion.rotate_vectors(rotation, coord_rotd)
    
    # Create new mask
    new_mask2 = np.zeros_like(new_mask)
    # Fix coordinates
    coord_rotd = fixCoordinates(coord_rotd, mask.shape)
    
    x1 = coord_rotd[:,0]
    y1 = coord_rotd[:,1]
    z1 = coord_rotd[:,2]
    
    # create new mask, similar as before
    new_mask2[x1, y1, z1] = True
    

    Given your example rotation, you can now plot both masks (that have the same shape), side by side:

    old mask and new mask